diff options
Diffstat (limited to 'crates/libanalysis')
-rw-r--r-- | crates/libanalysis/Cargo.toml | 12 | ||||
-rw-r--r-- | crates/libanalysis/src/lib.rs | 132 |
2 files changed, 144 insertions, 0 deletions
diff --git a/crates/libanalysis/Cargo.toml b/crates/libanalysis/Cargo.toml new file mode 100644 index 000000000..c773f4211 --- /dev/null +++ b/crates/libanalysis/Cargo.toml | |||
@@ -0,0 +1,12 @@ | |||
1 | [package] | ||
2 | name = "libanalysis" | ||
3 | version = "0.1.0" | ||
4 | authors = ["Aleksey Kladov <[email protected]>"] | ||
5 | |||
6 | [dependencies] | ||
7 | log = "0.4.2" | ||
8 | failure = "0.1.2" | ||
9 | parking_lot = "0.6.3" | ||
10 | once_cell = "0.1.4" | ||
11 | libsyntax2 = { path = "../libsyntax2" } | ||
12 | libeditor = { path = "../libeditor" } | ||
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 @@ | |||
1 | extern crate failure; | ||
2 | extern crate parking_lot; | ||
3 | #[macro_use] | ||
4 | extern crate log; | ||
5 | extern crate once_cell; | ||
6 | extern crate libsyntax2; | ||
7 | extern crate libeditor; | ||
8 | |||
9 | use once_cell::sync::OnceCell; | ||
10 | |||
11 | use std::{ | ||
12 | fs, | ||
13 | sync::Arc, | ||
14 | collections::hash_map::HashMap, | ||
15 | path::{PathBuf, Path}, | ||
16 | }; | ||
17 | use parking_lot::RwLock; | ||
18 | use libsyntax2::ast; | ||
19 | use libeditor::LineIndex; | ||
20 | |||
21 | pub type Result<T> = ::std::result::Result<T, ::failure::Error>; | ||
22 | |||
23 | pub struct WorldState { | ||
24 | data: Arc<WorldData> | ||
25 | } | ||
26 | |||
27 | pub struct World { | ||
28 | data: Arc<WorldData>, | ||
29 | } | ||
30 | |||
31 | impl 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 | |||
65 | impl 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)] | ||
123 | struct WorldData { | ||
124 | mem_map: HashMap<PathBuf, Arc<String>>, | ||
125 | file_map: RwLock<HashMap<PathBuf, Arc<FileData>>>, | ||
126 | } | ||
127 | |||
128 | struct FileData { | ||
129 | text: Option<String>, | ||
130 | syntax: OnceCell<ast::File>, | ||
131 | lines: OnceCell<LineIndex>, | ||
132 | } | ||