If I want to apply this globally, I could write a show rule
#show math.attach: it => {
show text: set text(font: "Libertinus serif", style: "italic")
it
}
$pi_"stuff"$
While this works, it is subtly different since all text in the base argument will also be affected. For example, something like {a "if" a < 5}_"stuff" will have if italicized.
Is there a way to only address the sub/supscripts and leaving the base argument unchanged?
A more general question would be whether it is possible to select an element based on what argument it was supplied to.
Yes, it does not seem to be possible. The underscore is part of the
syntax and creates a MathAttach Element.
One can override attach in math mode, but this will only alter the explicit
function call, e.g. attach(x, 5), but not x_5.
So the only other way I can think of is a show rule for math.attach. There one could
1. Rebuild the element and alter the arguments.
However if I don’t want to rebuild the whole attachment behaviour, I need to use math.attach inside the show rule as well, which will result in infinite recursion.
2. Alter the text of the arguments with show rules.
However the text does not “know” whether it is the base or the subscript.
One could check for equality with the base text, like
#show math.attach: it => {
show text: u => {
set text(font: "Libertinus serif", style: "italic") if u != it.base
u
}
it
}
but this is fragile and breaks at something like "stuff"_"stuff".
What a wonderful hack, i’ll have to remember that one! I am not sure about multiple identical labels though.
Fleshing it out could then look something like this
#show math.attach: it => {
if it.has("label") and it.label == <italicized-attach> {
return it
}
let (base, ..atts) = it.fields()
let attachment-text = text.with(font: "libertinus serif", style: "italic")
let is-number = s => s.match(regex("^\d+(\.\d+)?$")) != none
for (k, v) in atts.pairs() {
if v != none and v.func() == text and not is-number(v.text) {
atts.insert(k, attachment-text(v))
}
}
[#math.attach(base, ..atts)<italicized-attach>]
}
$"rho"_(3 5) "rho"_35 pi_f pi_"f" rho_5 pi_"stuff"^(a b c) med "stuff"_"stuff"$
Here I just map all text attachments.
Curiously, (as of Typst 0.13.1) decimal numbers are also parsed as text in math mode. I had a look in the parser to approximate this behaviour and add a special case to prevent numbers from being italicized.
Here is the snippet from typst/crates/typst-syntax/src/lexer.rs
fn math_text(&mut self, start: usize, c: char) -> SyntaxKind {
// Keep numbers and grapheme clusters together.
if c.is_numeric() {
self.s.eat_while(char::is_numeric);
let mut s = self.s;
if s.eat_if('.') && !s.eat_while(char::is_numeric).is_empty() {
self.s = s;
}
SyntaxKind::MathText
}
// [...]