How to get the content between two metadata()s?

#metadata("start:body") 

Jacob was here

#metadata("end:body")

The document is passed to a template (e.g. see here). How to get the content between those two tags?


Edit:
found a solution but it is too slow for larg(er) documents

let start_body = doc.children.position(i => i == metadata("start:body"))
let end_body = doc.children.position(i => i == metadata("end:body"))

let show_content = start_body != none and end_body != none and start_body < end_body

if show_content {
  body = doc.children.slice(start_body, end_body).join()
} 

I think there can be a better solution depending on what exactly you want to do with the extracted content.

As to your updated solution, this is about as good as it gets with the information given.

#show: doc => {
  doc
  let start_index = doc.children.position(child => child == metadata("start:body")) + 1
  let end_index = doc.children.position(child => child == metadata("end:body"))
  assert(start_index != none and end_index != none and start_index <= end_index)
  let content = doc.children.slice(start_index, end_index).join()
  [Captured content:]
  line(length: 100%)
  content
  line(length: 100%)
}

Text

#metadata("start:body")

Jacob was here

#metadata("end:body")

Text

image

2 Likes

So basically this is what I came up with. But as I mentioned it is way too slow.

At first I had different files for each “user-input-section” of my template. That means a file for each abstract, the main part of the document, and the appendix. But I don’t like to write text in different files. So I thought this would be a good solution. We had something like this in our Latex-template.

It doesn’t have to be a metadata-tag. I just want to write in the main document and control what goes where in the template with something like those tags.

Do you know a better solution? Those (multiple) iterations over doc.children are too much for real-time compilation.

So, are you absolutely positive that your template inputs must be written in a single file, correct? I think I had something similar once, and for readability I had to get one template/function input as include "somefile.typ":

#function(
  a: [...],
  b: include "file.typ",
  c: [...],
)

Oh, if you mean that you have template parameters to which you feed in the whole content of your document and nothing goes below:

// main.typ
#import "template.typ": template
#show: template.with(
  author: "...",
  chapter1: lorem(1000),
  chapter2: lorem(2000),
  appendix: lorem(100),
)

then this is probably not the most convenient/simple way to structure/write a document. Instead, you should include the parts directly (with or without include):

#lorem(10)
#include "chapter1.typ"
#include "chapter2.typ"
#include "appendix.typ"

Forcing Typst to split your file into artificial sections to then merge them anyway (without any preprocessing?) is a waste of resources. To achieve the best performance you should write/structure your document the Typst way.

I might have some additional tips if you can provide some additional information, like why you want to split your document in sections in the first place? You can always give a hypothetical/generalized structure of your document if you can’t share the whole thing.


As a side note, I’m pretty sure the questing has already been answered by yourself. So it’s better to later rephrase your question and the title or create a new question or general topic. I vote for a separate topic, because it can get very messy and not straightforward. You decide.

1 Like

They don’t have to be written in a single file, even though it would be nicer, in my opinion. I am now using includes as you suggested.

1 Like

Just as a note why there can’t be a general way to do this – what would happen if your content looked like this?

#metadata("start:body") 

#block[
  Jacob was

  #metadata("end:body")

  here
]

Should that work at all? Should the block be truncated? Two pieces of metadata don’t need to be “aligned” at the same nesting level, so this leads to problems.


An alternative approach that makes doing this impossible could look like this:

#lorem(10)

#[
  Jacob was here
] <body>

#lorem(10)

This is the saved content: #context query(<body>).first()

I.e. you put the body in a scope and give that whole thing a label. Naturally, this also doesn’t work over file boundaries; crossing file boundaries by its nature can’t guarantee that start and end are at the same nesting depth.

2 Likes