aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_db/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_db/src')
-rw-r--r--crates/ra_db/src/file_resolver.rs76
-rw-r--r--crates/ra_db/src/input.rs73
-rw-r--r--crates/ra_db/src/lib.rs83
-rw-r--r--crates/ra_db/src/loc2id.rs100
-rw-r--r--crates/ra_db/src/mock.rs51
-rw-r--r--crates/ra_db/src/syntax_ptr.rs48
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 @@
1use std::{
2 sync::Arc,
3 hash::{Hash, Hasher},
4 fmt,
5};
6
7use relative_path::RelativePath;
8
9use crate::input::FileId;
10
11pub 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)]
20pub struct FileResolverImp {
21 inner: Arc<FileResolver>,
22}
23
24impl PartialEq for FileResolverImp {
25 fn eq(&self, other: &FileResolverImp) -> bool {
26 self.inner() == other.inner()
27 }
28}
29
30impl Eq for FileResolverImp {}
31
32impl Hash for FileResolverImp {
33 fn hash<H: Hasher>(&self, hasher: &mut H) {
34 self.inner().hash(hasher);
35 }
36}
37
38impl 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
56impl 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 @@
1use std::sync::Arc;
2
3use rustc_hash::FxHashMap;
4use rustc_hash::FxHashSet;
5use salsa;
6
7use crate::file_resolver::FileResolverImp;
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
10pub struct FileId(pub u32);
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
13pub struct CrateId(pub u32);
14
15#[derive(Debug, Clone, Default, PartialEq, Eq)]
16pub struct CrateGraph {
17 pub(crate) crate_roots: FxHashMap<CrateId, FileId>,
18}
19
20impl 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
39salsa::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)]
65pub struct SourceRootId(pub u32);
66
67#[derive(Default, Clone, Debug, PartialEq, Eq)]
68pub struct SourceRoot {
69 pub file_resolver: FileResolverImp,
70 pub files: FxHashSet<FileId>,
71}
72
73pub 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.
2mod syntax_ptr;
3mod file_resolver;
4mod input;
5mod loc2id;
6pub mod mock;
7
8use std::sync::Arc;
9use ra_editor::LineIndex;
10use ra_syntax::{TextUnit, SourceFileNode};
11
12#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
13pub struct Canceled;
14
15pub type Cancelable<T> = Result<T, Canceled>;
16
17impl 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
23impl std::error::Error for Canceled {}
24
25pub 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]
36macro_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
49pub 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
59salsa::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
70fn source_file(db: &impl SyntaxDatabase, file_id: FileId) -> SourceFileNode {
71 let text = db.file_text(file_id);
72 SourceFileNode::parse(&*text)
73}
74fn 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)]
80pub 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 @@
1use parking_lot::Mutex;
2
3use std::hash::Hash;
4
5use 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)]
18struct Loc2IdMap<LOC, ID>
19where
20 ID: NumericId,
21 LOC: Clone + Eq + Hash,
22{
23 loc2id: FxHashMap<LOC, ID>,
24 id2loc: FxHashMap<ID, LOC>,
25}
26
27impl<LOC, ID> Default for Loc2IdMap<LOC, ID>
28where
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
40impl<LOC, ID> Loc2IdMap<LOC, ID>
41where
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
63pub trait NumericId: Clone + Eq + Hash {
64 fn from_u32(id: u32) -> Self;
65 fn to_u32(self) -> u32;
66}
67
68#[derive(Debug)]
69pub struct LocationIntener<LOC, ID>
70where
71 ID: NumericId,
72 LOC: Clone + Eq + Hash,
73{
74 map: Mutex<Loc2IdMap<LOC, ID>>,
75}
76
77impl<LOC, ID> Default for LocationIntener<LOC, ID>
78where
79 ID: NumericId,
80 LOC: Clone + Eq + Hash,
81{
82 fn default() -> Self {
83 LocationIntener {
84 map: Default::default(),
85 }
86 }
87}
88
89impl<LOC, ID> LocationIntener<LOC, ID>
90where
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 @@
1use std::sync::Arc;
2
3use rustc_hash::FxHashSet;
4use relative_path::{RelativePath, RelativePathBuf};
5
6use crate::{FileId, FileResolver, SourceRoot, FileResolverImp};
7
8#[derive(Default, Debug)]
9pub struct FileMap(Vec<(FileId, RelativePathBuf)>);
10
11impl 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
42impl 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 @@
1use 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)]
5pub struct LocalSyntaxPtr {
6 range: TextRange,
7 kind: SyntaxKind,
8}
9
10impl 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]
37fn 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}