1use crate::file::FileId;
2use crate::node::LinkedNode;
3use crate::parser::parse_with_offset;
4use crate::{parse, Span, SyntaxError, SyntaxNode};
5use std::path::PathBuf;
6use std::sync::Arc;
7
8#[derive(Clone, Debug)]
9pub struct Source(Arc<Repr>);
10
11#[derive(Clone, Debug)]
12struct Repr {
13 id: FileId,
14 text: String,
15 nodes: Vec<SyntaxNode>,
16 line_starts: Vec<usize>,
18}
19
20impl Source {
21 pub fn from_file(path: impl Into<PathBuf>, text: String) -> Self {
22 Self::new(FileId::new(path), text)
23 }
24
25 pub fn from_string(name: &str, text: String) -> Self {
26 Self::new(FileId::fake(name), text)
27 }
28
29 pub fn new(file_id: FileId, text: String) -> Self {
30 let line_starts = line_starts(&text, 0).collect();
31 let nodes = parse(&text, file_id);
32 Self(Arc::new(Repr {
33 line_starts,
34 id: file_id,
35 text,
36 nodes,
37 }))
38 }
39
40 pub fn id(&self) -> FileId {
41 self.0.id
42 }
43
44 pub fn text(&self) -> &str {
45 &self.0.text
46 }
47
48 pub fn span_text(&self, span: Span) -> Option<&str> {
49 if self.0.id != span.id()? {
50 return None;
51 }
52
53 self.0.text.get(span.range()?)
54 }
55
56 pub fn nodes(&self) -> &[SyntaxNode] {
57 &self.0.nodes
58 }
59
60 pub fn line_starts(&self) -> &[usize] {
61 &self.0.line_starts
62 }
63
64 pub fn append(&mut self, text: &str) {
65 let current_len = self.0.text.len();
66 let id = self.0.id;
67 let new_text = format!("{}{}", self.0.text, text);
68
69 let inner = Arc::make_mut(&mut self.0);
70
71 inner
73 .nodes
74 .extend(parse_with_offset(&new_text, id, current_len));
75 inner.line_starts = line_starts(&new_text, 0).collect();
76 inner.text = new_text;
77 }
78
79 pub fn warnings(&self) -> Vec<SyntaxError> {
80 self.nodes().iter().flat_map(SyntaxNode::warnings).collect()
81 }
82
83 pub fn find(&self, span: Span) -> Option<LinkedNode> {
84 for node in self.nodes() {
85 let linked_node = LinkedNode::new(node);
86 match linked_node.find(span) {
87 Some(node) => return Some(node),
88 None => continue,
89 }
90 }
91
92 None
93 }
94}
95
96pub fn line_starts(source: &str, offset: usize) -> impl '_ + Iterator<Item = usize> {
97 core::iter::once(0).chain(source.match_indices('\n').map(move |(i, _)| i + 1 + offset))
98}
99
100impl AsRef<str> for Source {
101 fn as_ref(&self) -> &str {
102 self.text()
103 }
104}