diff options
Diffstat (limited to 'crates/ra_db/src/lib.rs')
-rw-r--r-- | crates/ra_db/src/lib.rs | 168 |
1 files changed, 0 insertions, 168 deletions
diff --git a/crates/ra_db/src/lib.rs b/crates/ra_db/src/lib.rs deleted file mode 100644 index f25be24fe..000000000 --- a/crates/ra_db/src/lib.rs +++ /dev/null | |||
@@ -1,168 +0,0 @@ | |||
1 | //! ra_db defines basic database traits. The concrete DB is defined by ra_ide. | ||
2 | mod cancellation; | ||
3 | mod input; | ||
4 | pub mod fixture; | ||
5 | |||
6 | use std::{panic, sync::Arc}; | ||
7 | |||
8 | use ra_prof::profile; | ||
9 | use ra_syntax::{ast, Parse, SourceFile, TextRange, TextSize}; | ||
10 | use rustc_hash::FxHashSet; | ||
11 | |||
12 | pub use crate::{ | ||
13 | cancellation::Canceled, | ||
14 | input::{ | ||
15 | CrateData, CrateGraph, CrateId, CrateName, Dependency, Edition, Env, FileId, ProcMacroId, | ||
16 | SourceRoot, SourceRootId, | ||
17 | }, | ||
18 | }; | ||
19 | pub use salsa; | ||
20 | pub use vfs::{file_set::FileSet, VfsPath}; | ||
21 | |||
22 | #[macro_export] | ||
23 | macro_rules! impl_intern_key { | ||
24 | ($name:ident) => { | ||
25 | impl $crate::salsa::InternKey for $name { | ||
26 | fn from_intern_id(v: $crate::salsa::InternId) -> Self { | ||
27 | $name(v) | ||
28 | } | ||
29 | fn as_intern_id(&self) -> $crate::salsa::InternId { | ||
30 | self.0 | ||
31 | } | ||
32 | } | ||
33 | }; | ||
34 | } | ||
35 | |||
36 | pub trait Upcast<T: ?Sized> { | ||
37 | fn upcast(&self) -> &T; | ||
38 | } | ||
39 | |||
40 | pub trait CheckCanceled { | ||
41 | /// Aborts current query if there are pending changes. | ||
42 | /// | ||
43 | /// rust-analyzer needs to be able to answer semantic questions about the | ||
44 | /// code while the code is being modified. A common problem is that a | ||
45 | /// long-running query is being calculated when a new change arrives. | ||
46 | /// | ||
47 | /// We can't just apply the change immediately: this will cause the pending | ||
48 | /// query to see inconsistent state (it will observe an absence of | ||
49 | /// repeatable read). So what we do is we **cancel** all pending queries | ||
50 | /// before applying the change. | ||
51 | /// | ||
52 | /// We implement cancellation by panicking with a special value and catching | ||
53 | /// it on the API boundary. Salsa explicitly supports this use-case. | ||
54 | fn check_canceled(&self); | ||
55 | |||
56 | fn catch_canceled<F, T>(&self, f: F) -> Result<T, Canceled> | ||
57 | where | ||
58 | Self: Sized + panic::RefUnwindSafe, | ||
59 | F: FnOnce(&Self) -> T + panic::UnwindSafe, | ||
60 | { | ||
61 | panic::catch_unwind(|| f(self)).map_err(|err| match err.downcast::<Canceled>() { | ||
62 | Ok(canceled) => *canceled, | ||
63 | Err(payload) => panic::resume_unwind(payload), | ||
64 | }) | ||
65 | } | ||
66 | } | ||
67 | |||
68 | impl<T: salsa::Database> CheckCanceled for T { | ||
69 | fn check_canceled(&self) { | ||
70 | if self.salsa_runtime().is_current_revision_canceled() { | ||
71 | Canceled::throw() | ||
72 | } | ||
73 | } | ||
74 | } | ||
75 | |||
76 | #[derive(Clone, Copy, Debug)] | ||
77 | pub struct FilePosition { | ||
78 | pub file_id: FileId, | ||
79 | pub offset: TextSize, | ||
80 | } | ||
81 | |||
82 | #[derive(Clone, Copy, Debug, Eq, PartialEq)] | ||
83 | pub struct FileRange { | ||
84 | pub file_id: FileId, | ||
85 | pub range: TextRange, | ||
86 | } | ||
87 | |||
88 | pub const DEFAULT_LRU_CAP: usize = 128; | ||
89 | |||
90 | pub trait FileLoader { | ||
91 | /// Text of the file. | ||
92 | fn file_text(&self, file_id: FileId) -> Arc<String>; | ||
93 | /// Note that we intentionally accept a `&str` and not a `&Path` here. This | ||
94 | /// method exists to handle `#[path = "/some/path.rs"] mod foo;` and such, | ||
95 | /// so the input is guaranteed to be utf-8 string. One might be tempted to | ||
96 | /// introduce some kind of "utf-8 path with / separators", but that's a bad idea. Behold | ||
97 | /// `#[path = "C://no/way"]` | ||
98 | fn resolve_path(&self, anchor: FileId, path: &str) -> Option<FileId>; | ||
99 | fn relevant_crates(&self, file_id: FileId) -> Arc<FxHashSet<CrateId>>; | ||
100 | } | ||
101 | |||
102 | /// Database which stores all significant input facts: source code and project | ||
103 | /// model. Everything else in rust-analyzer is derived from these queries. | ||
104 | #[salsa::query_group(SourceDatabaseStorage)] | ||
105 | pub trait SourceDatabase: CheckCanceled + FileLoader + std::fmt::Debug { | ||
106 | // Parses the file into the syntax tree. | ||
107 | #[salsa::invoke(parse_query)] | ||
108 | fn parse(&self, file_id: FileId) -> Parse<ast::SourceFile>; | ||
109 | |||
110 | /// The crate graph. | ||
111 | #[salsa::input] | ||
112 | fn crate_graph(&self) -> Arc<CrateGraph>; | ||
113 | } | ||
114 | |||
115 | fn parse_query(db: &dyn SourceDatabase, file_id: FileId) -> Parse<ast::SourceFile> { | ||
116 | let _p = profile("parse_query").detail(|| format!("{:?}", file_id)); | ||
117 | let text = db.file_text(file_id); | ||
118 | SourceFile::parse(&*text) | ||
119 | } | ||
120 | |||
121 | /// We don't want to give HIR knowledge of source roots, hence we extract these | ||
122 | /// methods into a separate DB. | ||
123 | #[salsa::query_group(SourceDatabaseExtStorage)] | ||
124 | pub trait SourceDatabaseExt: SourceDatabase { | ||
125 | #[salsa::input] | ||
126 | fn file_text(&self, file_id: FileId) -> Arc<String>; | ||
127 | /// Path to a file, relative to the root of its source root. | ||
128 | /// Source root of the file. | ||
129 | #[salsa::input] | ||
130 | fn file_source_root(&self, file_id: FileId) -> SourceRootId; | ||
131 | /// Contents of the source root. | ||
132 | #[salsa::input] | ||
133 | fn source_root(&self, id: SourceRootId) -> Arc<SourceRoot>; | ||
134 | |||
135 | fn source_root_crates(&self, id: SourceRootId) -> Arc<FxHashSet<CrateId>>; | ||
136 | } | ||
137 | |||
138 | fn source_root_crates(db: &dyn SourceDatabaseExt, id: SourceRootId) -> Arc<FxHashSet<CrateId>> { | ||
139 | let graph = db.crate_graph(); | ||
140 | let res = graph | ||
141 | .iter() | ||
142 | .filter(|&krate| { | ||
143 | let root_file = graph[krate].root_file_id; | ||
144 | db.file_source_root(root_file) == id | ||
145 | }) | ||
146 | .collect::<FxHashSet<_>>(); | ||
147 | Arc::new(res) | ||
148 | } | ||
149 | |||
150 | /// Silly workaround for cyclic deps between the traits | ||
151 | pub struct FileLoaderDelegate<T>(pub T); | ||
152 | |||
153 | impl<T: SourceDatabaseExt> FileLoader for FileLoaderDelegate<&'_ T> { | ||
154 | fn file_text(&self, file_id: FileId) -> Arc<String> { | ||
155 | SourceDatabaseExt::file_text(self.0, file_id) | ||
156 | } | ||
157 | fn resolve_path(&self, anchor: FileId, path: &str) -> Option<FileId> { | ||
158 | // FIXME: this *somehow* should be platform agnostic... | ||
159 | let source_root = self.0.file_source_root(anchor); | ||
160 | let source_root = self.0.source_root(source_root); | ||
161 | source_root.file_set.resolve_path(anchor, path) | ||
162 | } | ||
163 | |||
164 | fn relevant_crates(&self, file_id: FileId) -> Arc<FxHashSet<CrateId>> { | ||
165 | let source_root = self.0.file_source_root(file_id); | ||
166 | self.0.source_root_crates(source_root) | ||
167 | } | ||
168 | } | ||