I’m relatively new to typst and am enjoying it so far, but am having a fundamental misunderstanding of something important.
To motivate my question, I propose that the following typst function calls are all the same and I would love to know why:
- #text(black)[my text]
- #text(11pt)[my text]
- #text(11pt,black)[my text]
- #text(black,11pt)[my text]
- #text(black,size:11pt)[my text]
- #text(size:11pt,black)[my text]
- #text(fill: black,size:11pt)[my text]
- #text(size:11pt,fill: black)[my text]
- #text("my text",size:11pt,fill: black)
- #text("my text",11pt,black)
- #text("my text",black,11pt)
- #text(black,"my text",11pt) // why does this work?
- #text(black,11pt,"my text") // why does this work?
- #text(fill:black,11pt,"my text") // why does this work?
- #text(fill:black,size:11pt,"my text") // why does this work?
When I look at the API documentation for the text function, I see a long list of named + non-required parameters followed by two positional + required parameters
I observe and assert (ready to be wrong):
#text(11pt)[my text] is taking in a content block as its required positional parameter and 11pt (of type length) as the named size: parameter though it seems like its being used a positional parameter.
#text(black)[my text] is taking in a content block as its required positional parameter and black (of type color) as the named fill: parameter though it seems like its being used a positional parameter.
fill: and size: seem to be the only named parameters for the text function that are psuedo-positional. (I do not mean this term to be disparaging – only a convenient neologism)
Both these (fill: and size:) are optional parameters because I know I can do a set text(10pt) or a set text(green) and another documentation states quite clearly that “Only optional parameters of that function can be provided to the set rule” (reference).
If the heart of these assertions is true, then my fundamental question is
How can I know (via documentation or code) which parameters are psuedo-positional?
Similar questions:
Why can I do #text(blue)[my text] but not #highlight(blue)[my text]? (both use fill: as optional parameters)
Is there any way to see documentation (or access to code) that would allow me to find this out for myself?
Without looking at the code and if I had to make a guess, it’ll be that the text function is popular enough to get some added (and undocumented) special behavior for the sake of convenience. Ultimately the only way to know exactly how it works is to take a look at the source code on GitHub.
i also found this on the changelog for 0.12.0-rc2, i dont know what conclusions to draw.
" Font family must be a named argument now: #set text(font: "..")"
i repeat that I’m not knowledgeable about typst, so take my answer with a spoonful of salt
The documentation for text indeed isn’t very clear about this, but here’s what’s going on:
The most common named arguments for text - mostly unambiguous ones such as fill: and size: - can also be used positionally as an exception, just for convenience. These parameters are not separately listed as positional in the documentation at the moment. Note that you can implement this same behavior in your functions by taking and parsing arbitrary amounts of arguments (see Arguments Type – Typst Documentation).
The two positional arguments you see (content and str) are currently misleading and need improvement. Basically, what’s going on here is that text is a special element, in that its element constructor (the #text(...)[stuff] function), instead of constructing a new text, applies the given parameters as local styles, as if you were applying a set rule on the given content. That is:
#text(red, font: "Arial")[Hello world!]
// is equivalent to
#[
#set text(red, font: "Arial")
Hello world!
]
This means that the whole #text() function (not the element itself, but its constructor) only exists as a convenience to apply text element set rules to the given content parameter, which is strictly positional. Therefore, that content you see is the content to apply the set rules to. (This is similar, for instance, to #align: #align(center)[abc] is more or less equivalent to #[ #set align(center); #block[abc]].)
But then what is the str parameter? That’s where the text element part comes in: the realtext elements are simple wrappers around strings. They contain no formatting at all - they are the most basic element of all text, so it is natural that any formatting would be done by elements (such as strong or emph) containing those more fundamental units, and not the other way around. So, this str “parameter” is actually the .text field of text elements (the contained text) “leaking” into the docs, but you can’t actually pass that parameter because you cannot construct text elements by yourself: they are automatically generated for you as you type text in a markup block. This means that #text("ABC") is actually the exact same as #text[ABC] which is also the exact same as #[ABC], since text, when used as a function, only applies styles to the text elements in ABC, but no styles are being configured so nothing happens. It’d certainly be much nicer if this could be displayed properly!
You’d have to look for arguments marked with #[parse] in the internal code, since those basically take the whole argument list into account (basically the same idea as using ..args in a function and manually extracting arguments however you want, as I hinted at above). Then, they might be pseudo-positional, or might accept more than one name (as seen in #pad(x: 5pt) being equivalent to #pad(left: 5pt, right: 5pt)), and so on.
text is simply an exception as it’s such a basic element. The same exception was not applied to highlight.