compose_syntax/
source.rs

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    /// The byte indexes of the start of each line
17    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        // parse the newly added text
72        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}