1 //! Defines a unit of change that can applied to a state of IDE to get the next
2 //! state. Changes are transactional.
4 use std::{fmt, sync::Arc, time};
7 salsa::{Database, Durability, SweepStrategy},
8 CrateGraph, FileId, SourceDatabase, SourceDatabaseExt, SourceRoot, SourceRootId,
10 use ra_prof::{memory_usage, profile, Bytes};
11 use rustc_hash::FxHashSet;
13 use crate::{symbol_index::SymbolsDatabase, RootDatabase};
16 pub struct AnalysisChange {
17 roots: Option<Vec<SourceRoot>>,
18 files_changed: Vec<(FileId, Option<Arc<String>>)>,
19 crate_graph: Option<CrateGraph>,
22 impl 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);
28 if !self.files_changed.is_empty() {
29 d.field("files_changed", &self.files_changed.len());
31 if self.crate_graph.is_some() {
32 d.field("crate_graph", &self.crate_graph);
39 pub fn new() -> AnalysisChange {
40 AnalysisChange::default()
43 pub fn set_roots(&mut self, roots: Vec<SourceRoot>) {
44 self.roots = Some(roots);
47 pub fn change_file(&mut self, file_id: FileId, new_text: Option<Arc<String>>) {
48 self.files_changed.push((file_id, new_text))
51 pub fn set_crate_graph(&mut self, graph: CrateGraph) {
52 self.crate_graph = Some(graph);
72 removed: Vec<RemoveFile>,
75 impl 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())
84 const GC_COOLDOWN: time::Duration = time::Duration::from_millis(100);
87 pub fn request_cancellation(&mut self) {
88 let _p = profile("RootDatabase::request_cancellation");
89 self.salsa_runtime_mut().synthetic_write(Durability::LOW);
92 pub fn apply_change(&mut self, change: AnalysisChange) {
93 let _p = profile("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);
103 library_roots.insert(root_id);
105 local_roots.insert(root_id);
107 for file_id in root.iter() {
108 self.set_file_source_root_with_durability(file_id, root_id, durability);
110 self.set_source_root_with_durability(root_id, Arc::new(root), durability);
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);
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)
124 if let Some(crate_graph) = change.crate_graph {
125 self.set_crate_graph_with_durability(Arc::new(crate_graph), Durability::HIGH)
129 pub fn maybe_collect_garbage(&mut self) {
130 if cfg!(feature = "wasm") {
134 if self.last_gc_check.elapsed() > GC_COOLDOWN {
135 self.last_gc_check = crate::wasm_shims::Instant::now();
139 pub fn collect_garbage(&mut self) {
140 if cfg!(feature = "wasm") {
144 let _p = profile("RootDatabase::collect_garbage");
145 self.last_gc = crate::wasm_shims::Instant::now();
147 let sweep = SweepStrategy::default().discard_values().sweep_all_revisions();
149 ra_db::ParseQuery.in_db(self).sweep(sweep);
150 hir::db::ParseMacroQuery.in_db(self).sweep(sweep);
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::MacroArgQuery).sweep(sweep);
155 // self.query(hir::db::MacroExpandQuery).sweep(sweep);
157 hir::db::AstIdMapQuery.in_db(self).sweep(sweep);
159 hir::db::BodyWithSourceMapQuery.in_db(self).sweep(sweep);
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);
166 // Feature: Memory Usage
168 // Clears rust-analyzer's internal database and prints memory usage statistics.
171 // | Editor | Action Name
173 // | VS Code | **Rust Analyzer: Memory Usage (Clears Database)**
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 {
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));
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));
198 ra_db::SourceRootCratesQuery
201 hir::db::AstIdMapQuery
202 hir::db::MacroArgQuery
203 hir::db::MacroDefQuery
204 hir::db::ParseMacroQuery
205 hir::db::MacroExpandQuery
208 hir::db::ItemTreeQuery
209 hir::db::CrateDefMapQueryQuery
210 hir::db::StructDataQuery
211 hir::db::UnionDataQuery
212 hir::db::EnumDataQuery
213 hir::db::ImplDataQuery
214 hir::db::TraitDataQuery
215 hir::db::TypeAliasDataQuery
216 hir::db::FunctionDataQuery
217 hir::db::ConstDataQuery
218 hir::db::StaticDataQuery
219 hir::db::BodyWithSourceMapQuery
221 hir::db::ExprScopesQuery
222 hir::db::GenericParamsQuery
224 hir::db::ModuleLangItemsQuery
225 hir::db::CrateLangItemsQuery
226 hir::db::LangItemQuery
227 hir::db::DocumentationQuery
228 hir::db::ImportMapQuery
231 hir::db::InferQueryQuery
233 hir::db::ValueTyQuery
234 hir::db::ImplSelfTyQuery
235 hir::db::ImplTraitQuery
236 hir::db::FieldTypesQuery
237 hir::db::CallableItemSignatureQuery
238 hir::db::GenericPredicatesForParamQuery
239 hir::db::GenericPredicatesQuery
240 hir::db::GenericDefaultsQuery
241 hir::db::InherentImplsInCrateQuery
242 hir::db::TraitImplsInCrateQuery
243 hir::db::TraitImplsInDepsQuery
244 hir::db::AssociatedTyDataQuery
245 hir::db::TraitDatumQuery
246 hir::db::StructDatumQuery
247 hir::db::ImplDatumQuery
248 hir::db::AssociatedTyValueQuery
249 hir::db::TraitSolveQuery
250 hir::db::ReturnTypeImplTraitsQuery
253 crate::symbol_index::FileSymbolsQuery
256 crate::LineIndexQuery
259 // To collect interned data, we need to bump the revision counter by performing a synthetic
261 // We do this after collecting the non-interned queries to correctly attribute memory used
263 self.salsa_runtime_mut().synthetic_write(Durability::HIGH);
267 hir::db::InternMacroQuery
268 hir::db::InternEagerExpansionQuery
271 hir::db::InternFunctionQuery
272 hir::db::InternStructQuery
273 hir::db::InternUnionQuery
274 hir::db::InternEnumQuery
275 hir::db::InternConstQuery
276 hir::db::InternStaticQuery
277 hir::db::InternTraitQuery
278 hir::db::InternTypeAliasQuery
279 hir::db::InternImplQuery
282 hir::db::InternTypeCtorQuery
283 hir::db::InternTypeParamIdQuery
284 hir::db::InternChalkImplQuery
285 hir::db::InternAssocTyValueQuery
288 acc.sort_by_key(|it| std::cmp::Reverse(it.1));
293 fn durability(source_root: &SourceRoot) -> Durability {
294 if source_root.is_library {