1mod offset_cursor;
2mod typst_translator;
3
4use typst_translator::TypstTranslator;
5
6use harper_core::{Token, parsers::Parser};
7use itertools::Itertools;
8use typst_syntax::{
9 Source, SyntaxNode,
10 ast::{AstNode, Expr, Markup},
11};
12
13pub struct Typst;
15
16impl Parser for Typst {
17 fn parse(&self, source: &[char]) -> Vec<Token> {
18 let source_str: String = source.iter().collect();
19
20 let typst_document = Source::detached(source_str);
22 let typst_tree = Markup::from_untyped(typst_document.root())
23 .expect("Unable to create typst document from parsed tree!");
24
25 let parse_helper = TypstTranslator::new(&typst_document);
27 let mut buf = Vec::new();
28 let exprs = typst_tree.exprs().collect_vec();
29 let exprs = convert_parbreaks(&mut buf, &exprs);
30 parse_helper.parse_exprs(&exprs)
31 }
32}
33
34fn convert_parbreaks<'a>(buf: &'a mut Vec<SyntaxNode>, exprs: &'a [Expr]) -> Vec<Expr<'a>> {
40 *buf = exprs
42 .iter()
43 .map(|e| {
44 let mut node = SyntaxNode::placeholder(typst_syntax::SyntaxKind::Parbreak);
45 node.synthesize(e.span());
46 node
47 })
48 .collect_vec();
49
50 let should_parbreak = |e1, e2, e3| {
51 matches!(e2, Expr::Space(_))
52 && (matches!(e1, Expr::Heading(_) | Expr::ListItem(_))
53 || matches!(e3, Expr::Heading(_) | Expr::ListItem(_)))
54 };
55
56 let mut res: Vec<Expr> = Vec::new();
57 let mut last_element: Option<Expr> = None;
58 for ((i, expr), (_, next_expr)) in exprs.iter().enumerate().tuple_windows() {
59 let mut current_expr = *expr;
60 if let Some(last_element) = last_element
61 && should_parbreak(last_element, *expr, *next_expr)
62 {
63 let pbreak = typst_syntax::ast::Parbreak::from_untyped(&buf[i])
64 .expect("Unable to convert expression to Parbreak");
65 current_expr = Expr::Parbreak(pbreak);
66 }
67 res.push(current_expr);
68 last_element = Some(*expr)
69 }
70 if let Some(last) = exprs.iter().last() {
72 res.push(*last);
73 }
74
75 res
76}
77
78#[cfg(test)]
79mod tests {
80 use super::*;
81 use harper_core::parsers::StrParser;
82
83 #[test]
84 fn issue_1898() {
85 Typst.parse_str("#for ");
86 Typst.parse_str("#(.$#$$$. ");
87 Typst.parse_str("=#{m\"\".'m\"\"#p#");
88 }
89}