diff --git a/crates/typst-library/src/text/raw.rs b/crates/typst-library/src/text/raw.rs index 1b4a42236..a9d404140 100644 --- a/crates/typst-library/src/text/raw.rs +++ b/crates/typst-library/src/text/raw.rs @@ -9,6 +9,7 @@ use typst::diag::FileError; use typst::eval::Bytes; use typst::syntax::{self, LinkedNode}; use typst::util::option_eq; +use unicode_segmentation::UnicodeSegmentation; use super::{ FontFamily, FontList, Hyphenate, LinebreakElem, SmartQuoteElem, TextElem, TextSize, @@ -218,6 +219,21 @@ pub struct RawElem { #[internal] #[parse(theme_data.map(Some))] pub theme_data: Option, + + /// The size for a tab stop in spaces. A tab is replaced with enough spaces to + /// align with the next multiple of the size. + /// + /// ````example + /// #set raw(tab-size: 8) + /// ```tsv + /// Year Month Day + /// 2000 2 3 + /// 2001 2 1 + /// 2002 3 10 + /// ``` + /// ```` + #[default(2)] + pub tab_size: usize, } impl RawElem { @@ -247,7 +263,12 @@ impl Synthesize for RawElem { impl Show for RawElem { #[tracing::instrument(name = "RawElem::show", skip_all)] fn show(&self, _: &mut Vt, styles: StyleChain) -> SourceResult { - let text = self.text(); + let mut text = self.text(); + if text.contains('\t') { + let tab_size = RawElem::tab_size_in(styles); + text = align_tabs(&text, tab_size); + } + let lang = self .lang(styles) .as_ref() @@ -609,3 +630,33 @@ fn item( }, } } + +/// Replace tabs with spaces to align with multiples of `tab_size`. +fn align_tabs(text: &str, tab_size: usize) -> EcoString { + let replacement = " ".repeat(tab_size); + let divisor = tab_size.max(1); + let amount = text.chars().filter(|&c| c == '\t').count(); + + let mut res = EcoString::with_capacity(text.len() - amount + amount * tab_size); + let mut column = 0; + + for grapheme in text.graphemes(true) { + match grapheme { + "\t" => { + let required = tab_size - column % divisor; + res.push_str(&replacement[..required]); + column += required; + } + "\n" => { + res.push_str(grapheme); + column = 0; + } + _ => { + res.push_str(grapheme); + column += 1; + } + } + } + + res +} diff --git a/tests/ref/text/raw-tabs.png b/tests/ref/text/raw-tabs.png new file mode 100644 index 000000000..cac265e98 Binary files /dev/null and b/tests/ref/text/raw-tabs.png differ diff --git a/tests/typ/text/raw-tabs.typ b/tests/typ/text/raw-tabs.typ new file mode 100644 index 000000000..d31326c0e --- /dev/null +++ b/tests/typ/text/raw-tabs.typ @@ -0,0 +1,11 @@ +// Test tabs in raw code. + +--- +#set raw(tab-size: 8) + +```tsv +Year Month Day +2000 2 3 +2001 2 1 +2002 3 10 +```