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}