diff options
Diffstat (limited to 'crates/base_db')
-rw-r--r-- | crates/base_db/src/change.rs | 97 | ||||
-rw-r--r-- | crates/base_db/src/fixture.rs | 175 | ||||
-rw-r--r-- | crates/base_db/src/lib.rs | 2 |
3 files changed, 191 insertions, 83 deletions
diff --git a/crates/base_db/src/change.rs b/crates/base_db/src/change.rs new file mode 100644 index 000000000..043e03bba --- /dev/null +++ b/crates/base_db/src/change.rs | |||
@@ -0,0 +1,97 @@ | |||
1 | //! Defines a unit of change that can applied to the database to get the next | ||
2 | //! state. Changes are transactional. | ||
3 | |||
4 | use std::{fmt, sync::Arc}; | ||
5 | |||
6 | use rustc_hash::FxHashSet; | ||
7 | use salsa::Durability; | ||
8 | use vfs::FileId; | ||
9 | |||
10 | use crate::{CrateGraph, SourceDatabaseExt, SourceRoot, SourceRootId}; | ||
11 | |||
12 | /// Encapsulate a bunch of raw `.set` calls on the database. | ||
13 | #[derive(Default)] | ||
14 | pub struct Change { | ||
15 | pub roots: Option<Vec<SourceRoot>>, | ||
16 | pub files_changed: Vec<(FileId, Option<Arc<String>>)>, | ||
17 | pub crate_graph: Option<CrateGraph>, | ||
18 | } | ||
19 | |||
20 | impl fmt::Debug for Change { | ||
21 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { | ||
22 | let mut d = fmt.debug_struct("AnalysisChange"); | ||
23 | if let Some(roots) = &self.roots { | ||
24 | d.field("roots", roots); | ||
25 | } | ||
26 | if !self.files_changed.is_empty() { | ||
27 | d.field("files_changed", &self.files_changed.len()); | ||
28 | } | ||
29 | if self.crate_graph.is_some() { | ||
30 | d.field("crate_graph", &self.crate_graph); | ||
31 | } | ||
32 | d.finish() | ||
33 | } | ||
34 | } | ||
35 | |||
36 | impl Change { | ||
37 | pub fn new() -> Change { | ||
38 | Change::default() | ||
39 | } | ||
40 | |||
41 | pub fn set_roots(&mut self, roots: Vec<SourceRoot>) { | ||
42 | self.roots = Some(roots); | ||
43 | } | ||
44 | |||
45 | pub fn change_file(&mut self, file_id: FileId, new_text: Option<Arc<String>>) { | ||
46 | self.files_changed.push((file_id, new_text)) | ||
47 | } | ||
48 | |||
49 | pub fn set_crate_graph(&mut self, graph: CrateGraph) { | ||
50 | self.crate_graph = Some(graph); | ||
51 | } | ||
52 | |||
53 | pub fn apply(self, db: &mut dyn SourceDatabaseExt) { | ||
54 | let _p = profile::span("RootDatabase::apply_change"); | ||
55 | // db.request_cancellation(); | ||
56 | // log::info!("apply_change {:?}", change); | ||
57 | if let Some(roots) = self.roots { | ||
58 | let mut local_roots = FxHashSet::default(); | ||
59 | let mut library_roots = FxHashSet::default(); | ||
60 | for (idx, root) in roots.into_iter().enumerate() { | ||
61 | let root_id = SourceRootId(idx as u32); | ||
62 | let durability = durability(&root); | ||
63 | if root.is_library { | ||
64 | library_roots.insert(root_id); | ||
65 | } else { | ||
66 | local_roots.insert(root_id); | ||
67 | } | ||
68 | for file_id in root.iter() { | ||
69 | db.set_file_source_root_with_durability(file_id, root_id, durability); | ||
70 | } | ||
71 | db.set_source_root_with_durability(root_id, Arc::new(root), durability); | ||
72 | } | ||
73 | // db.set_local_roots_with_durability(Arc::new(local_roots), Durability::HIGH); | ||
74 | // db.set_library_roots_with_durability(Arc::new(library_roots), Durability::HIGH); | ||
75 | } | ||
76 | |||
77 | for (file_id, text) in self.files_changed { | ||
78 | let source_root_id = db.file_source_root(file_id); | ||
79 | let source_root = db.source_root(source_root_id); | ||
80 | let durability = durability(&source_root); | ||
81 | // XXX: can't actually remove the file, just reset the text | ||
82 | let text = text.unwrap_or_default(); | ||
83 | db.set_file_text_with_durability(file_id, text, durability) | ||
84 | } | ||
85 | if let Some(crate_graph) = self.crate_graph { | ||
86 | db.set_crate_graph_with_durability(Arc::new(crate_graph), Durability::HIGH) | ||
87 | } | ||
88 | } | ||
89 | } | ||
90 | |||
91 | fn durability(source_root: &SourceRoot) -> Durability { | ||
92 | if source_root.is_library { | ||
93 | Durability::HIGH | ||
94 | } else { | ||
95 | Durability::LOW | ||
96 | } | ||
97 | } | ||
diff --git a/crates/base_db/src/fixture.rs b/crates/base_db/src/fixture.rs index 5ff8ead0e..b7286fc7d 100644 --- a/crates/base_db/src/fixture.rs +++ b/crates/base_db/src/fixture.rs | |||
@@ -65,24 +65,26 @@ use test_utils::{extract_range_or_offset, Fixture, RangeOrOffset, CURSOR_MARKER} | |||
65 | use vfs::{file_set::FileSet, VfsPath}; | 65 | use vfs::{file_set::FileSet, VfsPath}; |
66 | 66 | ||
67 | use crate::{ | 67 | use crate::{ |
68 | input::CrateName, CrateGraph, CrateId, Edition, Env, FileId, FilePosition, SourceDatabaseExt, | 68 | input::CrateName, Change, CrateGraph, CrateId, Edition, Env, FileId, FilePosition, |
69 | SourceRoot, SourceRootId, | 69 | SourceDatabaseExt, SourceRoot, SourceRootId, |
70 | }; | 70 | }; |
71 | 71 | ||
72 | pub const WORKSPACE: SourceRootId = SourceRootId(0); | 72 | pub const WORKSPACE: SourceRootId = SourceRootId(0); |
73 | 73 | ||
74 | pub trait WithFixture: Default + SourceDatabaseExt + 'static { | 74 | pub trait WithFixture: Default + SourceDatabaseExt + 'static { |
75 | fn with_single_file(text: &str) -> (Self, FileId) { | 75 | fn with_single_file(text: &str) -> (Self, FileId) { |
76 | let fixture = ChangeFixture::parse(text); | ||
76 | let mut db = Self::default(); | 77 | let mut db = Self::default(); |
77 | let (_, files) = with_files(&mut db, text); | 78 | fixture.change.apply(&mut db); |
78 | assert_eq!(files.len(), 1); | 79 | assert_eq!(fixture.files.len(), 1); |
79 | (db, files[0]) | 80 | (db, fixture.files[0]) |
80 | } | 81 | } |
81 | 82 | ||
82 | fn with_files(ra_fixture: &str) -> Self { | 83 | fn with_files(ra_fixture: &str) -> Self { |
84 | let fixture = ChangeFixture::parse(ra_fixture); | ||
83 | let mut db = Self::default(); | 85 | let mut db = Self::default(); |
84 | let (pos, _) = with_files(&mut db, ra_fixture); | 86 | fixture.change.apply(&mut db); |
85 | assert!(pos.is_none()); | 87 | assert!(fixture.file_position.is_none()); |
86 | db | 88 | db |
87 | } | 89 | } |
88 | 90 | ||
@@ -96,9 +98,10 @@ pub trait WithFixture: Default + SourceDatabaseExt + 'static { | |||
96 | } | 98 | } |
97 | 99 | ||
98 | fn with_range_or_offset(ra_fixture: &str) -> (Self, FileId, RangeOrOffset) { | 100 | fn with_range_or_offset(ra_fixture: &str) -> (Self, FileId, RangeOrOffset) { |
101 | let fixture = ChangeFixture::parse(ra_fixture); | ||
99 | let mut db = Self::default(); | 102 | let mut db = Self::default(); |
100 | let (pos, _) = with_files(&mut db, ra_fixture); | 103 | fixture.change.apply(&mut db); |
101 | let (file_id, range_or_offset) = pos.unwrap(); | 104 | let (file_id, range_or_offset) = fixture.file_position.unwrap(); |
102 | (db, file_id, range_or_offset) | 105 | (db, file_id, range_or_offset) |
103 | } | 106 | } |
104 | 107 | ||
@@ -113,89 +116,95 @@ pub trait WithFixture: Default + SourceDatabaseExt + 'static { | |||
113 | 116 | ||
114 | impl<DB: SourceDatabaseExt + Default + 'static> WithFixture for DB {} | 117 | impl<DB: SourceDatabaseExt + Default + 'static> WithFixture for DB {} |
115 | 118 | ||
116 | fn with_files( | 119 | pub struct ChangeFixture { |
117 | db: &mut dyn SourceDatabaseExt, | 120 | pub file_position: Option<(FileId, RangeOrOffset)>, |
118 | fixture: &str, | 121 | pub files: Vec<FileId>, |
119 | ) -> (Option<(FileId, RangeOrOffset)>, Vec<FileId>) { | 122 | pub change: Change, |
120 | let fixture = Fixture::parse(fixture); | 123 | } |
121 | |||
122 | let mut files = Vec::new(); | ||
123 | let mut crate_graph = CrateGraph::default(); | ||
124 | let mut crates = FxHashMap::default(); | ||
125 | let mut crate_deps = Vec::new(); | ||
126 | let mut default_crate_root: Option<FileId> = None; | ||
127 | |||
128 | let mut file_set = FileSet::default(); | ||
129 | let source_root_id = WORKSPACE; | ||
130 | let source_root_prefix = "/".to_string(); | ||
131 | let mut file_id = FileId(0); | ||
132 | |||
133 | let mut file_position = None; | ||
134 | |||
135 | for entry in fixture { | ||
136 | let text = if entry.text.contains(CURSOR_MARKER) { | ||
137 | let (range_or_offset, text) = extract_range_or_offset(&entry.text); | ||
138 | assert!(file_position.is_none()); | ||
139 | file_position = Some((file_id, range_or_offset)); | ||
140 | text.to_string() | ||
141 | } else { | ||
142 | entry.text.clone() | ||
143 | }; | ||
144 | 124 | ||
145 | let meta = FileMeta::from(entry); | 125 | impl ChangeFixture { |
146 | assert!(meta.path.starts_with(&source_root_prefix)); | 126 | pub fn parse(ra_fixture: &str) -> ChangeFixture { |
127 | let fixture = Fixture::parse(ra_fixture); | ||
128 | let mut change = Change::new(); | ||
129 | |||
130 | let mut files = Vec::new(); | ||
131 | let mut crate_graph = CrateGraph::default(); | ||
132 | let mut crates = FxHashMap::default(); | ||
133 | let mut crate_deps = Vec::new(); | ||
134 | let mut default_crate_root: Option<FileId> = None; | ||
135 | let mut default_cfg = CfgOptions::default(); | ||
136 | |||
137 | let mut file_set = FileSet::default(); | ||
138 | let source_root_prefix = "/".to_string(); | ||
139 | let mut file_id = FileId(0); | ||
140 | |||
141 | let mut file_position = None; | ||
142 | |||
143 | for entry in fixture { | ||
144 | let text = if entry.text.contains(CURSOR_MARKER) { | ||
145 | let (range_or_offset, text) = extract_range_or_offset(&entry.text); | ||
146 | assert!(file_position.is_none()); | ||
147 | file_position = Some((file_id, range_or_offset)); | ||
148 | text.to_string() | ||
149 | } else { | ||
150 | entry.text.clone() | ||
151 | }; | ||
152 | |||
153 | let meta = FileMeta::from(entry); | ||
154 | assert!(meta.path.starts_with(&source_root_prefix)); | ||
155 | |||
156 | if let Some(krate) = meta.krate { | ||
157 | let crate_id = crate_graph.add_crate_root( | ||
158 | file_id, | ||
159 | meta.edition, | ||
160 | Some(krate.clone()), | ||
161 | meta.cfg, | ||
162 | meta.env, | ||
163 | Default::default(), | ||
164 | ); | ||
165 | let crate_name = CrateName::new(&krate).unwrap(); | ||
166 | let prev = crates.insert(crate_name.clone(), crate_id); | ||
167 | assert!(prev.is_none()); | ||
168 | for dep in meta.deps { | ||
169 | let dep = CrateName::new(&dep).unwrap(); | ||
170 | crate_deps.push((crate_name.clone(), dep)) | ||
171 | } | ||
172 | } else if meta.path == "/main.rs" || meta.path == "/lib.rs" { | ||
173 | assert!(default_crate_root.is_none()); | ||
174 | default_crate_root = Some(file_id); | ||
175 | default_cfg = meta.cfg; | ||
176 | } | ||
177 | |||
178 | change.change_file(file_id, Some(Arc::new(text))); | ||
179 | let path = VfsPath::new_virtual_path(meta.path); | ||
180 | file_set.insert(file_id, path.into()); | ||
181 | files.push(file_id); | ||
182 | file_id.0 += 1; | ||
183 | } | ||
147 | 184 | ||
148 | if let Some(krate) = meta.krate { | 185 | if crates.is_empty() { |
149 | let crate_id = crate_graph.add_crate_root( | 186 | let crate_root = default_crate_root.unwrap(); |
150 | file_id, | 187 | crate_graph.add_crate_root( |
151 | meta.edition, | 188 | crate_root, |
152 | Some(krate.clone()), | 189 | Edition::Edition2018, |
153 | meta.cfg, | 190 | Some("test".to_string()), |
154 | meta.env, | 191 | default_cfg, |
192 | Env::default(), | ||
155 | Default::default(), | 193 | Default::default(), |
156 | ); | 194 | ); |
157 | let crate_name = CrateName::new(&krate).unwrap(); | 195 | } else { |
158 | let prev = crates.insert(crate_name.clone(), crate_id); | 196 | for (from, to) in crate_deps { |
159 | assert!(prev.is_none()); | 197 | let from_id = crates[&from]; |
160 | for dep in meta.deps { | 198 | let to_id = crates[&to]; |
161 | let dep = CrateName::new(&dep).unwrap(); | 199 | crate_graph.add_dep(from_id, CrateName::new(&to).unwrap(), to_id).unwrap(); |
162 | crate_deps.push((crate_name.clone(), dep)) | ||
163 | } | 200 | } |
164 | } else if meta.path == "/main.rs" || meta.path == "/lib.rs" { | ||
165 | assert!(default_crate_root.is_none()); | ||
166 | default_crate_root = Some(file_id); | ||
167 | } | 201 | } |
168 | 202 | ||
169 | db.set_file_text(file_id, Arc::new(text)); | 203 | change.set_roots(vec![SourceRoot::new_local(file_set)]); |
170 | db.set_file_source_root(file_id, source_root_id); | 204 | change.set_crate_graph(crate_graph); |
171 | let path = VfsPath::new_virtual_path(meta.path); | ||
172 | file_set.insert(file_id, path.into()); | ||
173 | files.push(file_id); | ||
174 | file_id.0 += 1; | ||
175 | } | ||
176 | 205 | ||
177 | if crates.is_empty() { | 206 | ChangeFixture { file_position, files, change } |
178 | let crate_root = default_crate_root.unwrap(); | ||
179 | crate_graph.add_crate_root( | ||
180 | crate_root, | ||
181 | Edition::Edition2018, | ||
182 | None, | ||
183 | CfgOptions::default(), | ||
184 | Env::default(), | ||
185 | Default::default(), | ||
186 | ); | ||
187 | } else { | ||
188 | for (from, to) in crate_deps { | ||
189 | let from_id = crates[&from]; | ||
190 | let to_id = crates[&to]; | ||
191 | crate_graph.add_dep(from_id, CrateName::new(&to).unwrap(), to_id).unwrap(); | ||
192 | } | ||
193 | } | 207 | } |
194 | |||
195 | db.set_source_root(source_root_id, Arc::new(SourceRoot::new_local(file_set))); | ||
196 | db.set_crate_graph(Arc::new(crate_graph)); | ||
197 | |||
198 | (file_position, files) | ||
199 | } | 208 | } |
200 | 209 | ||
201 | struct FileMeta { | 210 | struct FileMeta { |
diff --git a/crates/base_db/src/lib.rs b/crates/base_db/src/lib.rs index ee3415850..e38aa7257 100644 --- a/crates/base_db/src/lib.rs +++ b/crates/base_db/src/lib.rs | |||
@@ -1,6 +1,7 @@ | |||
1 | //! base_db defines basic database traits. The concrete DB is defined by ide. | 1 | //! base_db defines basic database traits. The concrete DB is defined by ide. |
2 | mod cancellation; | 2 | mod cancellation; |
3 | mod input; | 3 | mod input; |
4 | mod change; | ||
4 | pub mod fixture; | 5 | pub mod fixture; |
5 | 6 | ||
6 | use std::{panic, sync::Arc}; | 7 | use std::{panic, sync::Arc}; |
@@ -10,6 +11,7 @@ use syntax::{ast, Parse, SourceFile, TextRange, TextSize}; | |||
10 | 11 | ||
11 | pub use crate::{ | 12 | pub use crate::{ |
12 | cancellation::Canceled, | 13 | cancellation::Canceled, |
14 | change::Change, | ||
13 | input::{ | 15 | input::{ |
14 | CrateData, CrateGraph, CrateId, CrateName, Dependency, Edition, Env, FileId, ProcMacroId, | 16 | CrateData, CrateGraph, CrateId, CrateName, Dependency, Edition, Env, FileId, ProcMacroId, |
15 | SourceRoot, SourceRootId, | 17 | SourceRoot, SourceRootId, |