diff options
Diffstat (limited to 'crates/ra_db/src')
-rw-r--r-- | crates/ra_db/src/file_resolver.rs | 76 | ||||
-rw-r--r-- | crates/ra_db/src/input.rs | 73 | ||||
-rw-r--r-- | crates/ra_db/src/lib.rs | 83 | ||||
-rw-r--r-- | crates/ra_db/src/loc2id.rs | 100 | ||||
-rw-r--r-- | crates/ra_db/src/mock.rs | 51 | ||||
-rw-r--r-- | crates/ra_db/src/syntax_ptr.rs | 48 |
6 files changed, 431 insertions, 0 deletions
diff --git a/crates/ra_db/src/file_resolver.rs b/crates/ra_db/src/file_resolver.rs new file mode 100644 index 000000000..f849ac752 --- /dev/null +++ b/crates/ra_db/src/file_resolver.rs | |||
@@ -0,0 +1,76 @@ | |||
1 | use std::{ | ||
2 | sync::Arc, | ||
3 | hash::{Hash, Hasher}, | ||
4 | fmt, | ||
5 | }; | ||
6 | |||
7 | use relative_path::RelativePath; | ||
8 | |||
9 | use crate::input::FileId; | ||
10 | |||
11 | pub trait FileResolver: fmt::Debug + Send + Sync + 'static { | ||
12 | fn file_stem(&self, file_id: FileId) -> String; | ||
13 | fn resolve(&self, file_id: FileId, path: &RelativePath) -> Option<FileId>; | ||
14 | fn debug_path(&self, _1file_id: FileId) -> Option<std::path::PathBuf> { | ||
15 | None | ||
16 | } | ||
17 | } | ||
18 | |||
19 | #[derive(Clone, Debug)] | ||
20 | pub struct FileResolverImp { | ||
21 | inner: Arc<FileResolver>, | ||
22 | } | ||
23 | |||
24 | impl PartialEq for FileResolverImp { | ||
25 | fn eq(&self, other: &FileResolverImp) -> bool { | ||
26 | self.inner() == other.inner() | ||
27 | } | ||
28 | } | ||
29 | |||
30 | impl Eq for FileResolverImp {} | ||
31 | |||
32 | impl Hash for FileResolverImp { | ||
33 | fn hash<H: Hasher>(&self, hasher: &mut H) { | ||
34 | self.inner().hash(hasher); | ||
35 | } | ||
36 | } | ||
37 | |||
38 | impl FileResolverImp { | ||
39 | pub fn new(inner: Arc<FileResolver>) -> FileResolverImp { | ||
40 | FileResolverImp { inner } | ||
41 | } | ||
42 | pub fn file_stem(&self, file_id: FileId) -> String { | ||
43 | self.inner.file_stem(file_id) | ||
44 | } | ||
45 | pub fn resolve(&self, file_id: FileId, path: &RelativePath) -> Option<FileId> { | ||
46 | self.inner.resolve(file_id, path) | ||
47 | } | ||
48 | pub fn debug_path(&self, file_id: FileId) -> Option<std::path::PathBuf> { | ||
49 | self.inner.debug_path(file_id) | ||
50 | } | ||
51 | fn inner(&self) -> *const FileResolver { | ||
52 | &*self.inner | ||
53 | } | ||
54 | } | ||
55 | |||
56 | impl Default for FileResolverImp { | ||
57 | fn default() -> FileResolverImp { | ||
58 | #[derive(Debug)] | ||
59 | struct DummyResolver; | ||
60 | impl FileResolver for DummyResolver { | ||
61 | fn file_stem(&self, _file_: FileId) -> String { | ||
62 | panic!("file resolver not set") | ||
63 | } | ||
64 | fn resolve( | ||
65 | &self, | ||
66 | _file_id: FileId, | ||
67 | _path: &::relative_path::RelativePath, | ||
68 | ) -> Option<FileId> { | ||
69 | panic!("file resolver not set") | ||
70 | } | ||
71 | } | ||
72 | FileResolverImp { | ||
73 | inner: Arc::new(DummyResolver), | ||
74 | } | ||
75 | } | ||
76 | } | ||
diff --git a/crates/ra_db/src/input.rs b/crates/ra_db/src/input.rs new file mode 100644 index 000000000..9101ac7a8 --- /dev/null +++ b/crates/ra_db/src/input.rs | |||
@@ -0,0 +1,73 @@ | |||
1 | use std::sync::Arc; | ||
2 | |||
3 | use rustc_hash::FxHashMap; | ||
4 | use rustc_hash::FxHashSet; | ||
5 | use salsa; | ||
6 | |||
7 | use crate::file_resolver::FileResolverImp; | ||
8 | |||
9 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] | ||
10 | pub struct FileId(pub u32); | ||
11 | |||
12 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] | ||
13 | pub struct CrateId(pub u32); | ||
14 | |||
15 | #[derive(Debug, Clone, Default, PartialEq, Eq)] | ||
16 | pub struct CrateGraph { | ||
17 | pub(crate) crate_roots: FxHashMap<CrateId, FileId>, | ||
18 | } | ||
19 | |||
20 | impl CrateGraph { | ||
21 | pub fn crate_root(&self, crate_id: CrateId) -> FileId { | ||
22 | self.crate_roots[&crate_id] | ||
23 | } | ||
24 | pub fn add_crate_root(&mut self, file_id: FileId) -> CrateId { | ||
25 | let crate_id = CrateId(self.crate_roots.len() as u32); | ||
26 | let prev = self.crate_roots.insert(crate_id, file_id); | ||
27 | assert!(prev.is_none()); | ||
28 | crate_id | ||
29 | } | ||
30 | pub fn crate_id_for_crate_root(&self, file_id: FileId) -> Option<CrateId> { | ||
31 | let (&crate_id, _) = self | ||
32 | .crate_roots | ||
33 | .iter() | ||
34 | .find(|(_crate_id, &root_id)| root_id == file_id)?; | ||
35 | Some(crate_id) | ||
36 | } | ||
37 | } | ||
38 | |||
39 | salsa::query_group! { | ||
40 | pub trait FilesDatabase: salsa::Database { | ||
41 | fn file_text(file_id: FileId) -> Arc<String> { | ||
42 | type FileTextQuery; | ||
43 | storage input; | ||
44 | } | ||
45 | fn file_source_root(file_id: FileId) -> SourceRootId { | ||
46 | type FileSourceRootQuery; | ||
47 | storage input; | ||
48 | } | ||
49 | fn source_root(id: SourceRootId) -> Arc<SourceRoot> { | ||
50 | type SourceRootQuery; | ||
51 | storage input; | ||
52 | } | ||
53 | fn libraries() -> Arc<Vec<SourceRootId>> { | ||
54 | type LibrariesQuery; | ||
55 | storage input; | ||
56 | } | ||
57 | fn crate_graph() -> Arc<CrateGraph> { | ||
58 | type CrateGraphQuery; | ||
59 | storage input; | ||
60 | } | ||
61 | } | ||
62 | } | ||
63 | |||
64 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] | ||
65 | pub struct SourceRootId(pub u32); | ||
66 | |||
67 | #[derive(Default, Clone, Debug, PartialEq, Eq)] | ||
68 | pub struct SourceRoot { | ||
69 | pub file_resolver: FileResolverImp, | ||
70 | pub files: FxHashSet<FileId>, | ||
71 | } | ||
72 | |||
73 | pub const WORKSPACE: SourceRootId = SourceRootId(0); | ||
diff --git a/crates/ra_db/src/lib.rs b/crates/ra_db/src/lib.rs new file mode 100644 index 000000000..53805aada --- /dev/null +++ b/crates/ra_db/src/lib.rs | |||
@@ -0,0 +1,83 @@ | |||
1 | //! ra_db defines basic database traits. Concrete DB is defined by ra_analysis. | ||
2 | mod syntax_ptr; | ||
3 | mod file_resolver; | ||
4 | mod input; | ||
5 | mod loc2id; | ||
6 | pub mod mock; | ||
7 | |||
8 | use std::sync::Arc; | ||
9 | use ra_editor::LineIndex; | ||
10 | use ra_syntax::{TextUnit, SourceFileNode}; | ||
11 | |||
12 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] | ||
13 | pub struct Canceled; | ||
14 | |||
15 | pub type Cancelable<T> = Result<T, Canceled>; | ||
16 | |||
17 | impl std::fmt::Display for Canceled { | ||
18 | fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
19 | fmt.write_str("Canceled") | ||
20 | } | ||
21 | } | ||
22 | |||
23 | impl std::error::Error for Canceled {} | ||
24 | |||
25 | pub use crate::{ | ||
26 | syntax_ptr::LocalSyntaxPtr, | ||
27 | file_resolver::{FileResolver, FileResolverImp}, | ||
28 | input::{ | ||
29 | FilesDatabase, FileId, CrateId, SourceRoot, SourceRootId, CrateGraph, WORKSPACE, | ||
30 | FileTextQuery, FileSourceRootQuery, SourceRootQuery, LibrariesQuery, CrateGraphQuery, | ||
31 | }, | ||
32 | loc2id::{LocationIntener, NumericId}, | ||
33 | }; | ||
34 | |||
35 | #[macro_export] | ||
36 | macro_rules! impl_numeric_id { | ||
37 | ($id:ident) => { | ||
38 | impl $crate::NumericId for $id { | ||
39 | fn from_u32(id: u32) -> Self { | ||
40 | $id(id) | ||
41 | } | ||
42 | fn to_u32(self) -> u32 { | ||
43 | self.0 | ||
44 | } | ||
45 | } | ||
46 | }; | ||
47 | } | ||
48 | |||
49 | pub trait BaseDatabase: salsa::Database { | ||
50 | fn check_canceled(&self) -> Cancelable<()> { | ||
51 | if self.salsa_runtime().is_current_revision_canceled() { | ||
52 | Err(Canceled) | ||
53 | } else { | ||
54 | Ok(()) | ||
55 | } | ||
56 | } | ||
57 | } | ||
58 | |||
59 | salsa::query_group! { | ||
60 | pub trait SyntaxDatabase: crate::input::FilesDatabase + BaseDatabase { | ||
61 | fn source_file(file_id: FileId) -> SourceFileNode { | ||
62 | type SourceFileQuery; | ||
63 | } | ||
64 | fn file_lines(file_id: FileId) -> Arc<LineIndex> { | ||
65 | type FileLinesQuery; | ||
66 | } | ||
67 | } | ||
68 | } | ||
69 | |||
70 | fn source_file(db: &impl SyntaxDatabase, file_id: FileId) -> SourceFileNode { | ||
71 | let text = db.file_text(file_id); | ||
72 | SourceFileNode::parse(&*text) | ||
73 | } | ||
74 | fn file_lines(db: &impl SyntaxDatabase, file_id: FileId) -> Arc<LineIndex> { | ||
75 | let text = db.file_text(file_id); | ||
76 | Arc::new(LineIndex::new(&*text)) | ||
77 | } | ||
78 | |||
79 | #[derive(Clone, Copy, Debug)] | ||
80 | pub struct FilePosition { | ||
81 | pub file_id: FileId, | ||
82 | pub offset: TextUnit, | ||
83 | } | ||
diff --git a/crates/ra_db/src/loc2id.rs b/crates/ra_db/src/loc2id.rs new file mode 100644 index 000000000..69ba43d0f --- /dev/null +++ b/crates/ra_db/src/loc2id.rs | |||
@@ -0,0 +1,100 @@ | |||
1 | use parking_lot::Mutex; | ||
2 | |||
3 | use std::hash::Hash; | ||
4 | |||
5 | use rustc_hash::FxHashMap; | ||
6 | |||
7 | /// There are two principle ways to refer to things: | ||
8 | /// - by their locatinon (module in foo/bar/baz.rs at line 42) | ||
9 | /// - by their numeric id (module `ModuleId(42)`) | ||
10 | /// | ||
11 | /// The first one is more powerful (you can actually find the thing in question | ||
12 | /// by id), but the second one is so much more compact. | ||
13 | /// | ||
14 | /// `Loc2IdMap` allows us to have a cake an eat it as well: by maintaining a | ||
15 | /// bidirectional mapping between positional and numeric ids, we can use compact | ||
16 | /// representation wich still allows us to get the actual item | ||
17 | #[derive(Debug)] | ||
18 | struct Loc2IdMap<LOC, ID> | ||
19 | where | ||
20 | ID: NumericId, | ||
21 | LOC: Clone + Eq + Hash, | ||
22 | { | ||
23 | loc2id: FxHashMap<LOC, ID>, | ||
24 | id2loc: FxHashMap<ID, LOC>, | ||
25 | } | ||
26 | |||
27 | impl<LOC, ID> Default for Loc2IdMap<LOC, ID> | ||
28 | where | ||
29 | ID: NumericId, | ||
30 | LOC: Clone + Eq + Hash, | ||
31 | { | ||
32 | fn default() -> Self { | ||
33 | Loc2IdMap { | ||
34 | loc2id: FxHashMap::default(), | ||
35 | id2loc: FxHashMap::default(), | ||
36 | } | ||
37 | } | ||
38 | } | ||
39 | |||
40 | impl<LOC, ID> Loc2IdMap<LOC, ID> | ||
41 | where | ||
42 | ID: NumericId, | ||
43 | LOC: Clone + Eq + Hash, | ||
44 | { | ||
45 | pub fn loc2id(&mut self, loc: &LOC) -> ID { | ||
46 | match self.loc2id.get(loc) { | ||
47 | Some(id) => return id.clone(), | ||
48 | None => (), | ||
49 | } | ||
50 | let id = self.loc2id.len(); | ||
51 | assert!(id < u32::max_value() as usize); | ||
52 | let id = ID::from_u32(id as u32); | ||
53 | self.loc2id.insert(loc.clone(), id.clone()); | ||
54 | self.id2loc.insert(id.clone(), loc.clone()); | ||
55 | id | ||
56 | } | ||
57 | |||
58 | pub fn id2loc(&self, id: ID) -> LOC { | ||
59 | self.id2loc[&id].clone() | ||
60 | } | ||
61 | } | ||
62 | |||
63 | pub trait NumericId: Clone + Eq + Hash { | ||
64 | fn from_u32(id: u32) -> Self; | ||
65 | fn to_u32(self) -> u32; | ||
66 | } | ||
67 | |||
68 | #[derive(Debug)] | ||
69 | pub struct LocationIntener<LOC, ID> | ||
70 | where | ||
71 | ID: NumericId, | ||
72 | LOC: Clone + Eq + Hash, | ||
73 | { | ||
74 | map: Mutex<Loc2IdMap<LOC, ID>>, | ||
75 | } | ||
76 | |||
77 | impl<LOC, ID> Default for LocationIntener<LOC, ID> | ||
78 | where | ||
79 | ID: NumericId, | ||
80 | LOC: Clone + Eq + Hash, | ||
81 | { | ||
82 | fn default() -> Self { | ||
83 | LocationIntener { | ||
84 | map: Default::default(), | ||
85 | } | ||
86 | } | ||
87 | } | ||
88 | |||
89 | impl<LOC, ID> LocationIntener<LOC, ID> | ||
90 | where | ||
91 | ID: NumericId, | ||
92 | LOC: Clone + Eq + Hash, | ||
93 | { | ||
94 | pub fn loc2id(&self, loc: &LOC) -> ID { | ||
95 | self.map.lock().loc2id(loc) | ||
96 | } | ||
97 | pub fn id2loc(&self, id: ID) -> LOC { | ||
98 | self.map.lock().id2loc(id) | ||
99 | } | ||
100 | } | ||
diff --git a/crates/ra_db/src/mock.rs b/crates/ra_db/src/mock.rs new file mode 100644 index 000000000..2840f9655 --- /dev/null +++ b/crates/ra_db/src/mock.rs | |||
@@ -0,0 +1,51 @@ | |||
1 | use std::sync::Arc; | ||
2 | |||
3 | use rustc_hash::FxHashSet; | ||
4 | use relative_path::{RelativePath, RelativePathBuf}; | ||
5 | |||
6 | use crate::{FileId, FileResolver, SourceRoot, FileResolverImp}; | ||
7 | |||
8 | #[derive(Default, Debug)] | ||
9 | pub struct FileMap(Vec<(FileId, RelativePathBuf)>); | ||
10 | |||
11 | impl FileMap { | ||
12 | pub fn add(&mut self, path: RelativePathBuf) -> FileId { | ||
13 | let file_id = FileId((self.0.len() + 1) as u32); | ||
14 | self.0.push((file_id, path)); | ||
15 | file_id | ||
16 | } | ||
17 | |||
18 | pub fn into_source_root(self) -> SourceRoot { | ||
19 | let files = self.files(); | ||
20 | let file_resolver = FileResolverImp::new(Arc::new(self)); | ||
21 | SourceRoot { | ||
22 | file_resolver, | ||
23 | files, | ||
24 | } | ||
25 | } | ||
26 | |||
27 | pub fn files(&self) -> FxHashSet<FileId> { | ||
28 | self.iter().map(|(id, _)| id).collect() | ||
29 | } | ||
30 | |||
31 | fn iter<'a>(&'a self) -> impl Iterator<Item = (FileId, &'a RelativePath)> + 'a { | ||
32 | self.0 | ||
33 | .iter() | ||
34 | .map(|(id, path)| (*id, path.as_relative_path())) | ||
35 | } | ||
36 | |||
37 | fn path(&self, id: FileId) -> &RelativePath { | ||
38 | self.iter().find(|&(it, _)| it == id).unwrap().1 | ||
39 | } | ||
40 | } | ||
41 | |||
42 | impl FileResolver for FileMap { | ||
43 | fn file_stem(&self, id: FileId) -> String { | ||
44 | self.path(id).file_stem().unwrap().to_string() | ||
45 | } | ||
46 | fn resolve(&self, id: FileId, rel: &RelativePath) -> Option<FileId> { | ||
47 | let path = self.path(id).join(rel).normalize(); | ||
48 | let id = self.iter().find(|&(_, p)| path == p)?.0; | ||
49 | Some(id) | ||
50 | } | ||
51 | } | ||
diff --git a/crates/ra_db/src/syntax_ptr.rs b/crates/ra_db/src/syntax_ptr.rs new file mode 100644 index 000000000..dac94dd36 --- /dev/null +++ b/crates/ra_db/src/syntax_ptr.rs | |||
@@ -0,0 +1,48 @@ | |||
1 | use ra_syntax::{SourceFileNode, SyntaxKind, SyntaxNode, SyntaxNodeRef, TextRange}; | ||
2 | |||
3 | /// A pionter to a syntax node inside a file. | ||
4 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
5 | pub struct LocalSyntaxPtr { | ||
6 | range: TextRange, | ||
7 | kind: SyntaxKind, | ||
8 | } | ||
9 | |||
10 | impl LocalSyntaxPtr { | ||
11 | pub fn new(node: SyntaxNodeRef) -> LocalSyntaxPtr { | ||
12 | LocalSyntaxPtr { | ||
13 | range: node.range(), | ||
14 | kind: node.kind(), | ||
15 | } | ||
16 | } | ||
17 | |||
18 | pub fn resolve(self, file: &SourceFileNode) -> SyntaxNode { | ||
19 | let mut curr = file.syntax(); | ||
20 | loop { | ||
21 | if curr.range() == self.range && curr.kind() == self.kind { | ||
22 | return curr.owned(); | ||
23 | } | ||
24 | curr = curr | ||
25 | .children() | ||
26 | .find(|it| self.range.is_subrange(&it.range())) | ||
27 | .unwrap_or_else(|| panic!("can't resolve local ptr to SyntaxNode: {:?}", self)) | ||
28 | } | ||
29 | } | ||
30 | |||
31 | pub fn range(self) -> TextRange { | ||
32 | self.range | ||
33 | } | ||
34 | } | ||
35 | |||
36 | #[test] | ||
37 | fn test_local_syntax_ptr() { | ||
38 | use ra_syntax::{ast, AstNode}; | ||
39 | let file = SourceFileNode::parse("struct Foo { f: u32, }"); | ||
40 | let field = file | ||
41 | .syntax() | ||
42 | .descendants() | ||
43 | .find_map(ast::NamedFieldDef::cast) | ||
44 | .unwrap(); | ||
45 | let ptr = LocalSyntaxPtr::new(field.syntax()); | ||
46 | let field_syntax = ptr.resolve(&file); | ||
47 | assert_eq!(field.syntax(), field_syntax); | ||
48 | } | ||