1use std::fmt::{self, Debug, Formatter};
4use std::ops::Range;
5use std::sync::Arc;
6
7use typst_utils::LazyHash;
8
9use crate::lines::Lines;
10use crate::reparser::reparse;
11use crate::{
12 FileId, LinkedNode, RootedPath, Span, SpanNumber, SubRange, SyntaxNode, VirtualPath,
13 VirtualRoot, parse,
14};
15
16#[derive(Clone, Hash)]
24pub struct Source(Arc<LazyHash<SourceInner>>);
25
26#[derive(Clone, Hash)]
28struct SourceInner {
29 id: FileId,
30 root: SyntaxNode,
31 lines: Lines<String>,
32}
33
34impl Source {
35 pub fn new(id: FileId, text: String) -> Self {
37 let _scope = typst_timing::TimingScope::new("create source");
38 let mut root = parse(&text);
39 root.numberize(id, Span::FULL).unwrap();
40 Self(Arc::new(LazyHash::new(SourceInner { id, lines: Lines::new(text), root })))
41 }
42
43 pub fn detached(text: impl Into<String>) -> Self {
45 Self::new(
46 RootedPath::new(VirtualRoot::Project, VirtualPath::new("main.typ").unwrap())
47 .intern(),
48 text.into(),
49 )
50 }
51
52 pub fn with_root(id: FileId, text: String, root: SyntaxNode) -> Self {
54 Self(Arc::new(LazyHash::new(SourceInner { id, lines: Lines::new(text), root })))
55 }
56
57 pub fn root(&self) -> &SyntaxNode {
59 &self.0.root
60 }
61
62 pub fn id(&self) -> FileId {
64 self.0.id
65 }
66
67 pub fn text(&self) -> &str {
69 self.0.lines.text()
70 }
71
72 pub fn lines(&self) -> &Lines<String> {
75 &self.0.lines
76 }
77
78 pub fn replace(&mut self, new: &str) -> Range<usize> {
86 let _scope = typst_timing::TimingScope::new("replace source");
87
88 let Some((prefix, suffix)) = self.0.lines.replacement_range(new) else {
89 return 0..0;
90 };
91
92 let old = self.text();
93 let replace = prefix..old.len() - suffix;
94 let with = &new[prefix..new.len() - suffix];
95 self.edit(replace, with)
96 }
97
98 #[track_caller]
104 pub fn edit(&mut self, replace: Range<usize>, with: &str) -> Range<usize> {
105 let inner = &mut **Arc::make_mut(&mut self.0);
106
107 inner.lines.edit(replace.clone(), with);
109
110 reparse(&mut inner.root, inner.lines.text(), replace, with.len())
112 }
113
114 pub fn find(&self, span: Span) -> Option<LinkedNode<'_>> {
118 if span.id() != Some(self.id()) {
119 return None;
120 }
121 LinkedNode::new(self.root()).find(span)
122 }
123
124 pub fn range(
130 &self,
131 num: SpanNumber,
132 sub_range: Option<SubRange>,
133 ) -> Option<Range<usize>> {
134 let overall = LinkedNode::new(self.root()).find_number(num)?.range();
135 if let Some(sub_range) = sub_range {
136 let range = sub_range.to_absolute(overall.start);
137 assert!(range.end <= overall.end);
138 Some(range)
139 } else {
140 Some(overall)
141 }
142 }
143}
144
145impl Debug for Source {
146 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
147 write!(f, "Source({:?})", self.id().vpath())
148 }
149}
150
151impl AsRef<str> for Source {
152 fn as_ref(&self) -> &str {
153 self.text()
154 }
155}
156
157#[cfg(test)]
158mod test {
159 use super::Source;
160 use crate::{LinkedNode, Side, Span, SubRange};
161
162 #[test]
163 fn test_source_sub_ranges() {
164 let text = "= head <label>";
165 let source = Source::detached(text);
166 let get = |span: Span, sub_range| {
167 let num = crate::SpanNumber(span.number());
168 &text[source.range(num, sub_range).unwrap()]
169 };
170 let head = LinkedNode::new(source.root()).leaf_at(2, Side::After).unwrap().span();
171 assert_eq!(get(head, None), "head");
172 assert_eq!(get(head, SubRange::new(1, 3)), "ea");
173 assert_eq!(get(head, SubRange::new(0, 1)), "h");
174 assert_eq!(get(head, SubRange::new(0, 4)), "head");
175 assert_eq!(get(head, SubRange::new(3, 4)), "d");
176 let root = source.root().span();
177 assert_eq!(get(root, None), text);
178 assert_eq!(get(root, SubRange::new(3, 10)), "ead <la");
179 assert_eq!(get(root, SubRange::new(0, 10)), "= head <la");
180 assert_eq!(get(root, SubRange::new(3, 14)), "ead <label>");
181 }
182}