1use std::fmt::{self, Debug, Formatter};
4use std::hash::{Hash, Hasher};
5use std::ops::Range;
6use std::sync::Arc;
7
8use typst_utils::LazyHash;
9
10use crate::lines::Lines;
11use crate::reparser::reparse;
12use crate::{FileId, LinkedNode, Span, SyntaxNode, VirtualPath, parse};
13
14#[derive(Clone)]
21pub struct Source(Arc<Repr>);
22
23#[derive(Clone)]
25struct Repr {
26    id: FileId,
27    root: LazyHash<SyntaxNode>,
28    lines: LazyHash<Lines<String>>,
29}
30
31impl Source {
32    pub fn new(id: FileId, text: String) -> Self {
34        let _scope = typst_timing::TimingScope::new("create source");
35        let mut root = parse(&text);
36        root.numberize(id, Span::FULL).unwrap();
37        Self(Arc::new(Repr {
38            id,
39            lines: LazyHash::new(Lines::new(text)),
40            root: LazyHash::new(root),
41        }))
42    }
43
44    pub fn detached(text: impl Into<String>) -> Self {
46        Self::new(FileId::new(None, VirtualPath::new("main.typ")), text.into())
47    }
48
49    pub fn root(&self) -> &SyntaxNode {
51        &self.0.root
52    }
53
54    pub fn id(&self) -> FileId {
56        self.0.id
57    }
58
59    pub fn text(&self) -> &str {
61        self.0.lines.text()
62    }
63
64    pub fn lines(&self) -> &Lines<String> {
67        &self.0.lines
68    }
69
70    pub fn replace(&mut self, new: &str) -> Range<usize> {
78        let _scope = typst_timing::TimingScope::new("replace source");
79
80        let Some((prefix, suffix)) = self.0.lines.replacement_range(new) else {
81            return 0..0;
82        };
83
84        let old = self.text();
85        let replace = prefix..old.len() - suffix;
86        let with = &new[prefix..new.len() - suffix];
87        self.edit(replace, with)
88    }
89
90    #[track_caller]
96    pub fn edit(&mut self, replace: Range<usize>, with: &str) -> Range<usize> {
97        let inner = Arc::make_mut(&mut self.0);
98
99        inner.lines.edit(replace.clone(), with);
101
102        reparse(&mut inner.root, inner.lines.text(), replace, with.len())
104    }
105
106    pub fn find(&self, span: Span) -> Option<LinkedNode<'_>> {
110        LinkedNode::new(self.root()).find(span)
111    }
112
113    pub fn range(&self, span: Span) -> Option<Range<usize>> {
119        Some(self.find(span)?.range())
120    }
121}
122
123impl Debug for Source {
124    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
125        write!(f, "Source({:?})", self.id().vpath())
126    }
127}
128
129impl Hash for Source {
130    fn hash<H: Hasher>(&self, state: &mut H) {
131        self.0.id.hash(state);
132        self.0.lines.hash(state);
133        self.0.root.hash(state);
134    }
135}
136
137impl AsRef<str> for Source {
138    fn as_ref(&self) -> &str {
139        self.text()
140    }
141}