aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_db
diff options
context:
space:
mode:
authorAleksey Kladov <[email protected]>2018-11-28 00:25:20 +0000
committerAleksey Kladov <[email protected]>2018-11-28 00:25:20 +0000
commit11168c464cd962af3336a2cc68295496066edd6c (patch)
tree2c3b0bceea0dcf092ae8bf9d16c1508295606b09 /crates/ra_db
parentb2de95879a8d48cc4077895376b0aaed1e972169 (diff)
move db basics to ra_db
This should allow to move hir to a separate crate
Diffstat (limited to 'crates/ra_db')
-rw-r--r--crates/ra_db/Cargo.toml16
-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.rs69
-rw-r--r--crates/ra_db/src/loc2id.rs100
-rw-r--r--crates/ra_db/src/syntax_ptr.rs48
6 files changed, 382 insertions, 0 deletions
diff --git a/crates/ra_db/Cargo.toml b/crates/ra_db/Cargo.toml
new file mode 100644
index 000000000..3bf2f635e
--- /dev/null
+++ b/crates/ra_db/Cargo.toml
@@ -0,0 +1,16 @@
1[package]
2edition = "2018"
3name = "ra_db"
4version = "0.1.0"
5authors = ["Aleksey Kladov <[email protected]>"]
6
7[dependencies]
8log = "0.4.5"
9relative-path = "0.4.0"
10salsa = "0.8.0"
11rustc-hash = "1.0"
12parking_lot = "0.6.4"
13id-arena = { git = "https://github.com/fitzgen/id-arena/", rev = "43ecd67" }
14ra_syntax = { path = "../ra_syntax" }
15ra_editor = { path = "../ra_editor" }
16test_utils = { path = "../test_utils" }
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..833f95eeb
--- /dev/null
+++ b/crates/ra_db/src/lib.rs
@@ -0,0 +1,69 @@
1//! ra_db defines basic database traits. Concrete DB is defined by ra_analysis.
2
3extern crate ra_editor;
4extern crate ra_syntax;
5extern crate relative_path;
6extern crate rustc_hash;
7extern crate salsa;
8
9mod syntax_ptr;
10mod file_resolver;
11mod input;
12mod loc2id;
13
14use std::sync::Arc;
15use ra_editor::LineIndex;
16use ra_syntax::SourceFileNode;
17
18#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
19pub struct Canceled;
20
21pub type Cancelable<T> = Result<T, Canceled>;
22
23impl std::fmt::Display for Canceled {
24 fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
25 fmt.write_str("Canceled")
26 }
27}
28
29impl std::error::Error for Canceled {}
30
31pub use crate::{
32 syntax_ptr::LocalSyntaxPtr,
33 file_resolver::{FileResolver, FileResolverImp},
34 input::{
35 FilesDatabase, FileId, CrateId, SourceRoot, SourceRootId, CrateGraph, WORKSPACE,
36 FileTextQuery, FileSourceRootQuery, SourceRootQuery, LibrariesQuery, CrateGraphQuery,
37 },
38 loc2id::{LocationIntener, NumericId},
39};
40
41pub trait BaseDatabase: salsa::Database {
42 fn check_canceled(&self) -> Cancelable<()> {
43 if self.salsa_runtime().is_current_revision_canceled() {
44 Err(Canceled)
45 } else {
46 Ok(())
47 }
48 }
49}
50
51salsa::query_group! {
52 pub trait SyntaxDatabase: crate::input::FilesDatabase + BaseDatabase {
53 fn source_file(file_id: FileId) -> SourceFileNode {
54 type SourceFileQuery;
55 }
56 fn file_lines(file_id: FileId) -> Arc<LineIndex> {
57 type FileLinesQuery;
58 }
59 }
60}
61
62fn source_file(db: &impl SyntaxDatabase, file_id: FileId) -> SourceFileNode {
63 let text = db.file_text(file_id);
64 SourceFileNode::parse(&*text)
65}
66fn file_lines(db: &impl SyntaxDatabase, file_id: FileId) -> Arc<LineIndex> {
67 let text = db.file_text(file_id);
68 Arc::new(LineIndex::new(&*text))
69}
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/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}