aboutsummaryrefslogtreecommitdiff
path: root/crates/ide_db/src/change.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide_db/src/change.rs')
-rw-r--r--crates/ide_db/src/change.rs318
1 files changed, 318 insertions, 0 deletions
diff --git a/crates/ide_db/src/change.rs b/crates/ide_db/src/change.rs
new file mode 100644
index 000000000..8b4fd7ab8
--- /dev/null
+++ b/crates/ide_db/src/change.rs
@@ -0,0 +1,318 @@
1//! Defines a unit of change that can applied to a state of IDE to get the next
2//! state. Changes are transactional.
3
4use std::{fmt, sync::Arc, time};
5
6use base_db::{
7 salsa::{Database, Durability, SweepStrategy},
8 CrateGraph, FileId, SourceDatabase, SourceDatabaseExt, SourceRoot, SourceRootId,
9};
10use profile::{memory_usage, Bytes};
11use rustc_hash::FxHashSet;
12
13use crate::{symbol_index::SymbolsDatabase, RootDatabase};
14
15#[derive(Default)]
16pub struct AnalysisChange {
17 roots: Option<Vec<SourceRoot>>,
18 files_changed: Vec<(FileId, Option<Arc<String>>)>,
19 crate_graph: Option<CrateGraph>,
20}
21
22impl fmt::Debug for AnalysisChange {
23 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
24 let mut d = fmt.debug_struct("AnalysisChange");
25 if let Some(roots) = &self.roots {
26 d.field("roots", roots);
27 }
28 if !self.files_changed.is_empty() {
29 d.field("files_changed", &self.files_changed.len());
30 }
31 if self.crate_graph.is_some() {
32 d.field("crate_graph", &self.crate_graph);
33 }
34 d.finish()
35 }
36}
37
38impl AnalysisChange {
39 pub fn new() -> AnalysisChange {
40 AnalysisChange::default()
41 }
42
43 pub fn set_roots(&mut self, roots: Vec<SourceRoot>) {
44 self.roots = Some(roots);
45 }
46
47 pub fn change_file(&mut self, file_id: FileId, new_text: Option<Arc<String>>) {
48 self.files_changed.push((file_id, new_text))
49 }
50
51 pub fn set_crate_graph(&mut self, graph: CrateGraph) {
52 self.crate_graph = Some(graph);
53 }
54}
55
56#[derive(Debug)]
57struct AddFile {
58 file_id: FileId,
59 path: String,
60 text: Arc<String>,
61}
62
63#[derive(Debug)]
64struct RemoveFile {
65 file_id: FileId,
66 path: String,
67}
68
69#[derive(Default)]
70struct RootChange {
71 added: Vec<AddFile>,
72 removed: Vec<RemoveFile>,
73}
74
75impl fmt::Debug for RootChange {
76 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
77 fmt.debug_struct("AnalysisChange")
78 .field("added", &self.added.len())
79 .field("removed", &self.removed.len())
80 .finish()
81 }
82}
83
84const GC_COOLDOWN: time::Duration = time::Duration::from_millis(100);
85
86impl RootDatabase {
87 pub fn request_cancellation(&mut self) {
88 let _p = profile::span("RootDatabase::request_cancellation");
89 self.salsa_runtime_mut().synthetic_write(Durability::LOW);
90 }
91
92 pub fn apply_change(&mut self, change: AnalysisChange) {
93 let _p = profile::span("RootDatabase::apply_change");
94 self.request_cancellation();
95 log::info!("apply_change {:?}", change);
96 if let Some(roots) = change.roots {
97 let mut local_roots = FxHashSet::default();
98 let mut library_roots = FxHashSet::default();
99 for (idx, root) in roots.into_iter().enumerate() {
100 let root_id = SourceRootId(idx as u32);
101 let durability = durability(&root);
102 if root.is_library {
103 library_roots.insert(root_id);
104 } else {
105 local_roots.insert(root_id);
106 }
107 for file_id in root.iter() {
108 self.set_file_source_root_with_durability(file_id, root_id, durability);
109 }
110 self.set_source_root_with_durability(root_id, Arc::new(root), durability);
111 }
112 self.set_local_roots_with_durability(Arc::new(local_roots), Durability::HIGH);
113 self.set_library_roots_with_durability(Arc::new(library_roots), Durability::HIGH);
114 }
115
116 for (file_id, text) in change.files_changed {
117 let source_root_id = self.file_source_root(file_id);
118 let source_root = self.source_root(source_root_id);
119 let durability = durability(&source_root);
120 // XXX: can't actually remove the file, just reset the text
121 let text = text.unwrap_or_default();
122 self.set_file_text_with_durability(file_id, text, durability)
123 }
124 if let Some(crate_graph) = change.crate_graph {
125 self.set_crate_graph_with_durability(Arc::new(crate_graph), Durability::HIGH)
126 }
127 }
128
129 pub fn maybe_collect_garbage(&mut self) {
130 if cfg!(feature = "wasm") {
131 return;
132 }
133
134 if self.last_gc_check.elapsed() > GC_COOLDOWN {
135 self.last_gc_check = crate::wasm_shims::Instant::now();
136 }
137 }
138
139 pub fn collect_garbage(&mut self) {
140 if cfg!(feature = "wasm") {
141 return;
142 }
143
144 let _p = profile::span("RootDatabase::collect_garbage");
145 self.last_gc = crate::wasm_shims::Instant::now();
146
147 let sweep = SweepStrategy::default().discard_values().sweep_all_revisions();
148
149 base_db::ParseQuery.in_db(self).sweep(sweep);
150 hir::db::ParseMacroQuery.in_db(self).sweep(sweep);
151
152 // Macros do take significant space, but less then the syntax trees
153 // self.query(hir::db::MacroDefQuery).sweep(sweep);
154 // self.query(hir::db::MacroArgTextQuery).sweep(sweep);
155 // self.query(hir::db::MacroExpandQuery).sweep(sweep);
156
157 hir::db::AstIdMapQuery.in_db(self).sweep(sweep);
158
159 hir::db::BodyWithSourceMapQuery.in_db(self).sweep(sweep);
160
161 hir::db::ExprScopesQuery.in_db(self).sweep(sweep);
162 hir::db::InferQueryQuery.in_db(self).sweep(sweep);
163 hir::db::BodyQuery.in_db(self).sweep(sweep);
164 }
165
166 // Feature: Memory Usage
167 //
168 // Clears rust-analyzer's internal database and prints memory usage statistics.
169 //
170 // |===
171 // | Editor | Action Name
172 //
173 // | VS Code | **Rust Analyzer: Memory Usage (Clears Database)**
174 // |===
175 pub fn per_query_memory_usage(&mut self) -> Vec<(String, Bytes)> {
176 let mut acc: Vec<(String, Bytes)> = vec![];
177 let sweep = SweepStrategy::default().discard_values().sweep_all_revisions();
178 macro_rules! sweep_each_query {
179 ($($q:path)*) => {$(
180 let before = memory_usage().allocated;
181 $q.in_db(self).sweep(sweep);
182 let after = memory_usage().allocated;
183 let q: $q = Default::default();
184 let name = format!("{:?}", q);
185 acc.push((name, before - after));
186
187 let before = memory_usage().allocated;
188 $q.in_db(self).sweep(sweep.discard_everything());
189 let after = memory_usage().allocated;
190 let q: $q = Default::default();
191 let name = format!("{:?} (deps)", q);
192 acc.push((name, before - after));
193
194 let before = memory_usage().allocated;
195 $q.in_db(self).purge();
196 let after = memory_usage().allocated;
197 let q: $q = Default::default();
198 let name = format!("{:?} (purge)", q);
199 acc.push((name, before - after));
200 )*}
201 }
202 sweep_each_query![
203 // SourceDatabase
204 base_db::ParseQuery
205 base_db::CrateGraphQuery
206
207 // SourceDatabaseExt
208 base_db::FileTextQuery
209 base_db::FileSourceRootQuery
210 base_db::SourceRootQuery
211 base_db::SourceRootCratesQuery
212
213 // AstDatabase
214 hir::db::AstIdMapQuery
215 hir::db::MacroArgTextQuery
216 hir::db::MacroDefQuery
217 hir::db::ParseMacroQuery
218 hir::db::MacroExpandQuery
219
220 // DefDatabase
221 hir::db::ItemTreeQuery
222 hir::db::CrateDefMapQueryQuery
223 hir::db::StructDataQuery
224 hir::db::UnionDataQuery
225 hir::db::EnumDataQuery
226 hir::db::ImplDataQuery
227 hir::db::TraitDataQuery
228 hir::db::TypeAliasDataQuery
229 hir::db::FunctionDataQuery
230 hir::db::ConstDataQuery
231 hir::db::StaticDataQuery
232 hir::db::BodyWithSourceMapQuery
233 hir::db::BodyQuery
234 hir::db::ExprScopesQuery
235 hir::db::GenericParamsQuery
236 hir::db::AttrsQuery
237 hir::db::ModuleLangItemsQuery
238 hir::db::CrateLangItemsQuery
239 hir::db::LangItemQuery
240 hir::db::DocumentationQuery
241 hir::db::ImportMapQuery
242
243 // HirDatabase
244 hir::db::InferQueryQuery
245 hir::db::TyQuery
246 hir::db::ValueTyQuery
247 hir::db::ImplSelfTyQuery
248 hir::db::ImplTraitQuery
249 hir::db::FieldTypesQuery
250 hir::db::CallableItemSignatureQuery
251 hir::db::GenericPredicatesForParamQuery
252 hir::db::GenericPredicatesQuery
253 hir::db::GenericDefaultsQuery
254 hir::db::InherentImplsInCrateQuery
255 hir::db::TraitImplsInCrateQuery
256 hir::db::TraitImplsInDepsQuery
257 hir::db::AssociatedTyDataQuery
258 hir::db::AssociatedTyDataQuery
259 hir::db::TraitDatumQuery
260 hir::db::StructDatumQuery
261 hir::db::ImplDatumQuery
262 hir::db::FnDefDatumQuery
263 hir::db::ReturnTypeImplTraitsQuery
264 hir::db::InternCallableDefQuery
265 hir::db::InternTypeParamIdQuery
266 hir::db::InternImplTraitIdQuery
267 hir::db::InternClosureQuery
268 hir::db::AssociatedTyValueQuery
269 hir::db::TraitSolveQuery
270
271 // SymbolsDatabase
272 crate::symbol_index::FileSymbolsQuery
273 crate::symbol_index::LibrarySymbolsQuery
274 crate::symbol_index::LocalRootsQuery
275 crate::symbol_index::LibraryRootsQuery
276
277 // LineIndexDatabase
278 crate::LineIndexQuery
279 ];
280
281 // To collect interned data, we need to bump the revision counter by performing a synthetic
282 // write.
283 // We do this after collecting the non-interned queries to correctly attribute memory used
284 // by interned data.
285 self.salsa_runtime_mut().synthetic_write(Durability::HIGH);
286
287 sweep_each_query![
288 // AstDatabase
289 hir::db::InternMacroQuery
290 hir::db::InternEagerExpansionQuery
291
292 // InternDatabase
293 hir::db::InternFunctionQuery
294 hir::db::InternStructQuery
295 hir::db::InternUnionQuery
296 hir::db::InternEnumQuery
297 hir::db::InternConstQuery
298 hir::db::InternStaticQuery
299 hir::db::InternTraitQuery
300 hir::db::InternTypeAliasQuery
301 hir::db::InternImplQuery
302
303 // HirDatabase
304 hir::db::InternTypeParamIdQuery
305 ];
306
307 acc.sort_by_key(|it| std::cmp::Reverse(it.1));
308 acc
309 }
310}
311
312fn durability(source_root: &SourceRoot) -> Durability {
313 if source_root.is_library {
314 Durability::HIGH
315 } else {
316 Durability::LOW
317 }
318}