aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_db/src/lib.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_db/src/lib.rs')
-rw-r--r--crates/ra_db/src/lib.rs168
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.
2mod cancellation;
3mod input;
4pub mod fixture;
5
6use std::{panic, sync::Arc};
7
8use ra_prof::profile;
9use ra_syntax::{ast, Parse, SourceFile, TextRange, TextSize};
10use rustc_hash::FxHashSet;
11
12pub use crate::{
13 cancellation::Canceled,
14 input::{
15 CrateData, CrateGraph, CrateId, CrateName, Dependency, Edition, Env, FileId, ProcMacroId,
16 SourceRoot, SourceRootId,
17 },
18};
19pub use salsa;
20pub use vfs::{file_set::FileSet, VfsPath};
21
22#[macro_export]
23macro_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
36pub trait Upcast<T: ?Sized> {
37 fn upcast(&self) -> &T;
38}
39
40pub 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
68impl<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)]
77pub struct FilePosition {
78 pub file_id: FileId,
79 pub offset: TextSize,
80}
81
82#[derive(Clone, Copy, Debug, Eq, PartialEq)]
83pub struct FileRange {
84 pub file_id: FileId,
85 pub range: TextRange,
86}
87
88pub const DEFAULT_LRU_CAP: usize = 128;
89
90pub 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)]
105pub 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
115fn 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)]
124pub 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
138fn 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
151pub struct FileLoaderDelegate<T>(pub T);
152
153impl<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}