aboutsummaryrefslogtreecommitdiff
path: root/crates/base_db
diff options
context:
space:
mode:
Diffstat (limited to 'crates/base_db')
-rw-r--r--crates/base_db/src/change.rs97
-rw-r--r--crates/base_db/src/fixture.rs175
-rw-r--r--crates/base_db/src/lib.rs2
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
4use std::{fmt, sync::Arc};
5
6use rustc_hash::FxHashSet;
7use salsa::Durability;
8use vfs::FileId;
9
10use crate::{CrateGraph, SourceDatabaseExt, SourceRoot, SourceRootId};
11
12/// Encapsulate a bunch of raw `.set` calls on the database.
13#[derive(Default)]
14pub 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
20impl 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
36impl 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
91fn 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}
65use vfs::{file_set::FileSet, VfsPath}; 65use vfs::{file_set::FileSet, VfsPath};
66 66
67use crate::{ 67use 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
72pub const WORKSPACE: SourceRootId = SourceRootId(0); 72pub const WORKSPACE: SourceRootId = SourceRootId(0);
73 73
74pub trait WithFixture: Default + SourceDatabaseExt + 'static { 74pub 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
114impl<DB: SourceDatabaseExt + Default + 'static> WithFixture for DB {} 117impl<DB: SourceDatabaseExt + Default + 'static> WithFixture for DB {}
115 118
116fn with_files( 119pub 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); 125impl 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
201struct FileMeta { 210struct 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.
2mod cancellation; 2mod cancellation;
3mod input; 3mod input;
4mod change;
4pub mod fixture; 5pub mod fixture;
5 6
6use std::{panic, sync::Arc}; 7use std::{panic, sync::Arc};
@@ -10,6 +11,7 @@ use syntax::{ast, Parse, SourceFile, TextRange, TextSize};
10 11
11pub use crate::{ 12pub 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,