Customizing source line break behavior
I don't know if this is possible and if there is demand but:
Current line breaking behavior
| source | compiled |
|---|---|
| \n | [ ] |
| \ | linebreak() |
| >1 times \n | parbreak() |
Want to be able to customize it to
| source | compiled |
|---|---|
| \n | linebreak() |
| > 1 times \n | parbreak() |
| \\n | continue line |
or maybe even
| source | compiled |
|---|---|
| \n | [ ] |
| \n\n | linebreak() |
| >2 times \n | parbreak() |
| \ | linebreak() |
Bonus if this behavior can be changed depending on the context (e.g. in a paragraph vs table cell) and if rules for trimming line breaks in certain contexts can be defined.
Doing this in Typst code right now is almost impossible, mostly because
- show rules like
#show regex("\\s"): white_char => ...never capture line breaks in the source - \n gets compiled to [ ] (making it indistinguishable from many other things that generate [ ])
- the content tree is currently not well traverseable and mutable.
Regarding the demand: one major use case would be typesetting poems. Those have manual line breaks by definition, and putting '\' at the end of every line quickly gets annoying. I'd gladly do something like
#let poem(
text
) = {
set par(join-lines: false)
text
}
I can imagine adding a set rule to respect line breaks in source and turn them into linebreak() instead of [ ].
Configurable \ for line continuation, in contrast, would in my opinion do more harm than good. It's really the opposite of what it does now and it would be hard to judge at a glance what a piece of code does. This problem already comes up a bit with the hard linebreaks, but there I see more of a use case.
I really dislike overriding symbol behaviour if not necessary.
Could a field be added to the Space node in the ast or just a new node be added? The source already has the comment
/// Whitespace in markup or math. Has at most one newline in markup, as more
/// indicate a paragraph break.
Maybe it would be best to keep the information about the type of whitespace in the node?
AST nodes already contain their complete text, so it's easy to find out the number of newlines in it.
I would also be interested in finding a solution to this. I expected that with the recent changes to the realisation subsystem, I could simply #show "\n": linebreak() but that doesn't work.
The show rule doesn't operate on the source text but on the realized text. In the case of your show rule, there are no "\n" in the realized text, so it doesn't match. (If it did match, it would replace a linebreak with a linebreak, to no real effect.)
Regarding the demand: one major use case would be typesetting poems. Those have manual line breaks by definition, and putting '' at the end of every line quickly gets annoying. I'd gladly do something like
#let poem( text ) = { set par(join-lines: false) text }
I wonder if any work could be done in this direction? I really enjoy using Typst and, in particular, would like to expand its usage to poetry. Such a feature would be of huge help, because so far editing a poem requires manually add a lot of line breaks.
one major use case would be typesetting poems
Workaround
#show raw.where(lang: "poem"): it => {
set text(font: "Source Serif Pro") // Override the monospace
it.text.split("\n\n").map(
paragraph => paragraph.split("\n").join(linebreak())
).join(parbreak())
}
#lorem(10)
#quote(block: true, attribution: link("https://en.wikisource.org/wiki/Shakespeare%27s_Songs/Blow,_Blow,_Thou_Winter_Wind")[_As You Like It_])[
```poem
Blow, blow, thou winter wind,
Thou art not so unkind,
As man's ingratitude;
Thy tooth is not so keen,
Because thou art not seen,
Although thy breath be rude.
High-ho! sing, heigh-ho! unto the green holly:
Most friendship is feigning, most loving mere folly:
Then heigh-ho, the holly!
This life is most jolly.
Freeze, freeze, thou bitter sky,
Thou dost not bite so nigh
As benefits forgot:
Though thou the waters warp,
Thy sting is not so sharp
As friend remembcr'd not.
Heigh-ho! sing, heigh-ho! unto the green holly:
Most friendship is feigning, most loving mere folly:
Then heigh-ho, the holly!
This life is most jolly.
```
]
#lorem(10)
@YDX-2147483647
Nice idea for simple cases, but it's not unusual to add some commands inside, like footnotes and markings.
Here is another poem + raw block workaround that allows most inline markup: https://forum.typst.app/t/how-to-force-line-breaks-without-extra-spaces/4091/10 I think it still speaks for a native "environment" that works similar to this, i.e preserving linebreaks. It would have benefits like proper editor support for the embedded markup.
I have another use-case for this as well.
I am rendering a label for use in a laboratory and the users are not expected to be generally familiar with Typst, but may use some of its intuitive typesetting (bold, italics, etc). More advanced users will be able to input chemical equations using #ce from typsium or something similar.
My approach to this is to eval(..., mode="markup") an input string, but I would like for this to break on newlines without appending a \ to the end of each line. I have so far simply replaced newlines in the input string with #let input_str = input_str.replace("\n", "\\\n"), but this does not work for multiline code, since the resulting \ will be a syntax error in that case.
My current workaround is to instead do #let input_str = input_str.replace("\n", "\n\n") and add a set par(spacing: ...) to make paragraph breaks be similar size to line breaks. This is okay for my use case since there really isn't room for paragraphs on the labels anyway, but is admittedly a bit of a hack. A native solution would be preferable.
Edit: My workaround has an unfortunate limitation, which a solution to this issue would alleviate: The user cannot enter empty lines, as they simply are not rendered as linebreaks.