|
| 1 | +use std::{mem, sync::Arc}; |
| 2 | + |
| 3 | +use indexing::IndexingResult; |
| 4 | +use rowan::ast::AstNode; |
| 5 | +use rustc_hash::{FxHashMap, FxHashSet}; |
| 6 | +use syntax::cst; |
| 7 | + |
| 8 | +use crate::files::FileId; |
| 9 | + |
1 | 10 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] |
2 | 11 | pub enum QueryKey { |
3 | | - Index(usize), |
4 | | - Resolve(usize), |
| 12 | + Content(FileId), |
| 13 | + Parse(FileId), |
| 14 | + Index(FileId), |
| 15 | +} |
| 16 | + |
| 17 | +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] |
| 18 | +pub struct Trace { |
| 19 | + built: usize, |
| 20 | + changed: usize, |
| 21 | + dependencies: Arc<[QueryKey]>, |
| 22 | +} |
| 23 | + |
| 24 | +impl Trace { |
| 25 | + pub fn create_input(revision: usize) -> Trace { |
| 26 | + Trace { built: revision, changed: revision, dependencies: [].into() } |
| 27 | + } |
| 28 | + |
| 29 | + pub fn create_fresh(revision: usize, dependencies: Arc<[QueryKey]>) -> Trace { |
| 30 | + Trace { built: revision, changed: revision, dependencies } |
| 31 | + } |
| 32 | +} |
| 33 | + |
| 34 | +#[derive(Default)] |
| 35 | +pub struct Runtime { |
| 36 | + revision: usize, |
| 37 | + traces: FxHashMap<QueryKey, Trace>, |
| 38 | + |
| 39 | + content: FxHashMap<FileId, Arc<str>>, |
| 40 | + parse: FxHashMap<FileId, cst::Module>, |
| 41 | + index: FxHashMap<FileId, Arc<IndexingResult>>, |
| 42 | + |
| 43 | + parent: Option<QueryKey>, |
| 44 | + dependencies: FxHashMap<QueryKey, FxHashSet<QueryKey>>, |
| 45 | +} |
| 46 | + |
| 47 | +impl Runtime { |
| 48 | + pub fn compute<T>(&mut self, k: QueryKey, compute: impl Fn(&mut Runtime) -> T) -> T { |
| 49 | + let parent = mem::replace(&mut self.parent, Some(k)); |
| 50 | + let result = compute(self); |
| 51 | + |
| 52 | + self.revision += 1; |
| 53 | + let revision = self.revision; |
| 54 | + |
| 55 | + let dependencies = self |
| 56 | + .dependencies |
| 57 | + .get(&k) |
| 58 | + .map(|dependencies| dependencies.iter().copied()) |
| 59 | + .unwrap_or_default() |
| 60 | + .collect(); |
| 61 | + |
| 62 | + let v = Trace::create_fresh(revision, dependencies); |
| 63 | + self.traces.insert(k, v); |
| 64 | + |
| 65 | + self.parent = parent; |
| 66 | + result |
| 67 | + } |
| 68 | + |
| 69 | + pub fn query<T: Clone>( |
| 70 | + &mut self, |
| 71 | + k: QueryKey, |
| 72 | + compute: impl Fn(&mut Runtime) -> T, |
| 73 | + get_storage: impl Fn(&mut Runtime) -> Option<(T, &mut Trace)>, |
| 74 | + set_storage: impl Fn(&mut Runtime, T), |
| 75 | + ) -> T { |
| 76 | + if let Some(parent) = self.parent { |
| 77 | + self.dependencies.entry(parent).or_default().insert(k); |
| 78 | + } |
| 79 | + |
| 80 | + let revision = self.revision; |
| 81 | + if let Some((value, trace)) = get_storage(self) { |
| 82 | + if trace.built == revision { |
| 83 | + value |
| 84 | + } else { |
| 85 | + let built = trace.built; |
| 86 | + let dependencies = Arc::clone(&trace.dependencies); |
| 87 | + |
| 88 | + let mut latest = 0; |
| 89 | + for dependency in dependencies.iter() { |
| 90 | + match dependency { |
| 91 | + QueryKey::Content(_) => (), |
| 92 | + QueryKey::Parse(id) => { |
| 93 | + self.parse(*id); |
| 94 | + } |
| 95 | + QueryKey::Index(_) => (), |
| 96 | + } |
| 97 | + if let Some(dependency) = self.traces.get(dependency) { |
| 98 | + latest = latest.max(dependency.changed); |
| 99 | + } |
| 100 | + } |
| 101 | + |
| 102 | + if built >= latest { |
| 103 | + if let Some(trace) = self.traces.get_mut(&k) { |
| 104 | + trace.built = revision; |
| 105 | + } |
| 106 | + value |
| 107 | + } else { |
| 108 | + let fresh = self.compute(k, compute); |
| 109 | + set_storage(self, T::clone(&fresh)); |
| 110 | + fresh |
| 111 | + } |
| 112 | + } |
| 113 | + } else { |
| 114 | + let fresh = self.compute(k, compute); |
| 115 | + set_storage(self, T::clone(&fresh)); |
| 116 | + fresh |
| 117 | + } |
| 118 | + } |
| 119 | + |
| 120 | + pub fn set_content(&mut self, id: FileId, content: Arc<str>) { |
| 121 | + self.revision += 1; |
| 122 | + let revision = self.revision; |
| 123 | + |
| 124 | + self.content.insert(id, content); |
| 125 | + |
| 126 | + let k = QueryKey::Content(id); |
| 127 | + let v = Trace::create_input(revision); |
| 128 | + self.traces.insert(k, v); |
| 129 | + } |
| 130 | + |
| 131 | + pub fn content(&mut self, id: FileId) -> Arc<str> { |
| 132 | + let k = QueryKey::Content(id); |
| 133 | + if let Some(parent) = self.parent { |
| 134 | + self.dependencies.entry(parent).or_default().insert(k); |
| 135 | + } |
| 136 | + let v = self.content.get(&id).expect("invalid violated: invalid query key"); |
| 137 | + Arc::clone(v) |
| 138 | + } |
| 139 | + |
| 140 | + pub fn parse(&mut self, id: FileId) -> cst::Module { |
| 141 | + let k = QueryKey::Parse(id); |
| 142 | + self.query( |
| 143 | + k, |
| 144 | + |this| { |
| 145 | + let content = this.content(id); |
| 146 | + |
| 147 | + let lexed = lexing::lex(&content); |
| 148 | + let tokens = lexing::layout(&lexed); |
| 149 | + |
| 150 | + let (node, _) = parsing::parse(&lexed, &tokens); |
| 151 | + cst::Module::cast(node).expect("invariant violated: cannot cast parse result") |
| 152 | + }, |
| 153 | + |this| { |
| 154 | + let value = this.parse.get(&id).cloned()?; |
| 155 | + let trace = this.traces.get_mut(&k)?; |
| 156 | + Some((value, trace)) |
| 157 | + }, |
| 158 | + |this, value| { |
| 159 | + this.parse.insert(id, value); |
| 160 | + }, |
| 161 | + ) |
| 162 | + } |
| 163 | + |
| 164 | + pub fn index(&mut self, id: FileId) -> Arc<IndexingResult> { |
| 165 | + let k = QueryKey::Index(id); |
| 166 | + self.query( |
| 167 | + k, |
| 168 | + |this| { |
| 169 | + let module = this.parse(id); |
| 170 | + let (result, _) = indexing::index(&module); |
| 171 | + Arc::new(result) |
| 172 | + }, |
| 173 | + |this| { |
| 174 | + let value = this.index.get(&id).cloned()?; |
| 175 | + let trace = this.traces.get_mut(&k)?; |
| 176 | + Some((value, trace)) |
| 177 | + }, |
| 178 | + |this, value| { |
| 179 | + this.index.insert(id, value); |
| 180 | + }, |
| 181 | + ) |
| 182 | + } |
| 183 | +} |
| 184 | + |
| 185 | +#[cfg(test)] |
| 186 | +mod tests { |
| 187 | + use crate::files::Files; |
| 188 | + |
| 189 | + use super::Runtime; |
| 190 | + |
| 191 | + #[test] |
| 192 | + fn test_basic() { |
| 193 | + let mut runtime = Runtime::default(); |
| 194 | + let mut files = Files::default(); |
| 195 | + |
| 196 | + let id = files.insert("./src/Main.purs", "module Main where\n\nlife = 42"); |
| 197 | + let content = files.content(id); |
| 198 | + |
| 199 | + runtime.set_content(id, content); |
| 200 | + dbg!(runtime.index(id)); |
| 201 | + dbg!(runtime.index(id)); |
| 202 | + |
| 203 | + runtime.set_content(id, "module Main where\n\n\n\nlife = 42".into()); |
| 204 | + dbg!(runtime.index(id)); |
| 205 | + dbg!(runtime.index(id)); |
| 206 | + } |
5 | 207 | } |
0 commit comments