aboutsummaryrefslogtreecommitdiff
path: root/crates/libanalysis/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/libanalysis/src')
-rw-r--r--crates/libanalysis/src/lib.rs132
1 files changed, 132 insertions, 0 deletions
diff --git a/crates/libanalysis/src/lib.rs b/crates/libanalysis/src/lib.rs
new file mode 100644
index 000000000..6a946a0b0
--- /dev/null
+++ b/crates/libanalysis/src/lib.rs
@@ -0,0 +1,132 @@
1extern crate failure;
2extern crate parking_lot;
3#[macro_use]
4extern crate log;
5extern crate once_cell;
6extern crate libsyntax2;
7extern crate libeditor;
8
9use once_cell::sync::OnceCell;
10
11use std::{
12 fs,
13 sync::Arc,
14 collections::hash_map::HashMap,
15 path::{PathBuf, Path},
16};
17use parking_lot::RwLock;
18use libsyntax2::ast;
19use libeditor::LineIndex;
20
21pub type Result<T> = ::std::result::Result<T, ::failure::Error>;
22
23pub struct WorldState {
24 data: Arc<WorldData>
25}
26
27pub struct World {
28 data: Arc<WorldData>,
29}
30
31impl WorldState {
32 pub fn new() -> WorldState {
33 WorldState {
34 data: Arc::new(WorldData::default())
35 }
36 }
37
38 pub fn snapshot(&self) -> World {
39 World { data: self.data.clone() }
40 }
41
42 pub fn change_overlay(&mut self, path: PathBuf, text: Option<String>) {
43 let data = self.data_mut();
44 data.file_map.get_mut().remove(&path);
45 if let Some(text) = text {
46 data.mem_map.insert(path, Arc::new(text));
47 } else {
48 data.mem_map.remove(&path);
49 }
50 }
51
52 fn data_mut(&mut self) -> &mut WorldData {
53 if Arc::get_mut(&mut self.data).is_none() {
54 let file_map = self.data.file_map.read().clone();
55 self.data = Arc::new(WorldData {
56 mem_map: self.data.mem_map.clone(),
57 file_map: RwLock::new(file_map),
58 });
59 }
60 Arc::get_mut(&mut self.data).unwrap()
61 }
62}
63
64
65impl World {
66 pub fn file_syntax(&self, path: &Path) -> Result<ast::File> {
67 let data = self.file_data(path)?;
68 let syntax = data.syntax
69 .get_or_init(|| {
70 trace!("parsing: {}", path.display());
71 ast::File::parse(self.file_text(path, &data))
72 }).clone();
73 Ok(syntax)
74 }
75
76 pub fn file_line_index(&self, path: &Path) -> Result<LineIndex> {
77 let data = self.file_data(path)?;
78 let index = data.lines
79 .get_or_init(|| {
80 trace!("calc line index: {}", path.display());
81 LineIndex::new(self.file_text(path, &data))
82 });
83 Ok(index.clone())
84 }
85
86 fn file_text<'a>(&'a self, path: &Path, file_data: &'a FileData) -> &'a str {
87 match file_data.text.as_ref() {
88 Some(text) => text.as_str(),
89 None => self.data.mem_map[path].as_str()
90 }
91 }
92
93 fn file_data(&self, path: &Path) -> Result<Arc<FileData>> {
94 {
95 let guard = self.data.file_map.read();
96 if let Some(data) = guard.get(path) {
97 return Ok(data.clone());
98 }
99 }
100
101 let text = if self.data.mem_map.contains_key(path) {
102 None
103 } else {
104 trace!("loading file from disk: {}", path.display());
105 Some(fs::read_to_string(path)?)
106 };
107 let res = {
108 let mut guard = self.data.file_map.write();
109 guard.entry(path.to_owned())
110 .or_insert_with(|| Arc::new(FileData {
111 text,
112 syntax: OnceCell::new(),
113 lines: OnceCell::new(),
114 }))
115 .clone()
116 };
117 Ok(res)
118 }
119}
120
121
122#[derive(Default)]
123struct WorldData {
124 mem_map: HashMap<PathBuf, Arc<String>>,
125 file_map: RwLock<HashMap<PathBuf, Arc<FileData>>>,
126}
127
128struct FileData {
129 text: Option<String>,
130 syntax: OnceCell<ast::File>,
131 lines: OnceCell<LineIndex>,
132}