From 11168c464cd962af3336a2cc68295496066edd6c Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 28 Nov 2018 03:25:20 +0300 Subject: move db basics to ra_db This should allow to move hir to a separate crate --- crates/ra_db/src/file_resolver.rs | 76 +++++++++++++++++++++++++++++ crates/ra_db/src/input.rs | 73 ++++++++++++++++++++++++++++ crates/ra_db/src/lib.rs | 69 ++++++++++++++++++++++++++ crates/ra_db/src/loc2id.rs | 100 ++++++++++++++++++++++++++++++++++++++ crates/ra_db/src/syntax_ptr.rs | 48 ++++++++++++++++++ 5 files changed, 366 insertions(+) create mode 100644 crates/ra_db/src/file_resolver.rs create mode 100644 crates/ra_db/src/input.rs create mode 100644 crates/ra_db/src/lib.rs create mode 100644 crates/ra_db/src/loc2id.rs create mode 100644 crates/ra_db/src/syntax_ptr.rs (limited to 'crates/ra_db/src') 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 @@ +use std::{ + sync::Arc, + hash::{Hash, Hasher}, + fmt, +}; + +use relative_path::RelativePath; + +use crate::input::FileId; + +pub trait FileResolver: fmt::Debug + Send + Sync + 'static { + fn file_stem(&self, file_id: FileId) -> String; + fn resolve(&self, file_id: FileId, path: &RelativePath) -> Option; + fn debug_path(&self, _1file_id: FileId) -> Option { + None + } +} + +#[derive(Clone, Debug)] +pub struct FileResolverImp { + inner: Arc, +} + +impl PartialEq for FileResolverImp { + fn eq(&self, other: &FileResolverImp) -> bool { + self.inner() == other.inner() + } +} + +impl Eq for FileResolverImp {} + +impl Hash for FileResolverImp { + fn hash(&self, hasher: &mut H) { + self.inner().hash(hasher); + } +} + +impl FileResolverImp { + pub fn new(inner: Arc) -> FileResolverImp { + FileResolverImp { inner } + } + pub fn file_stem(&self, file_id: FileId) -> String { + self.inner.file_stem(file_id) + } + pub fn resolve(&self, file_id: FileId, path: &RelativePath) -> Option { + self.inner.resolve(file_id, path) + } + pub fn debug_path(&self, file_id: FileId) -> Option { + self.inner.debug_path(file_id) + } + fn inner(&self) -> *const FileResolver { + &*self.inner + } +} + +impl Default for FileResolverImp { + fn default() -> FileResolverImp { + #[derive(Debug)] + struct DummyResolver; + impl FileResolver for DummyResolver { + fn file_stem(&self, _file_: FileId) -> String { + panic!("file resolver not set") + } + fn resolve( + &self, + _file_id: FileId, + _path: &::relative_path::RelativePath, + ) -> Option { + panic!("file resolver not set") + } + } + FileResolverImp { + inner: Arc::new(DummyResolver), + } + } +} 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 @@ +use std::sync::Arc; + +use rustc_hash::FxHashMap; +use rustc_hash::FxHashSet; +use salsa; + +use crate::file_resolver::FileResolverImp; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct FileId(pub u32); + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct CrateId(pub u32); + +#[derive(Debug, Clone, Default, PartialEq, Eq)] +pub struct CrateGraph { + pub(crate) crate_roots: FxHashMap, +} + +impl CrateGraph { + pub fn crate_root(&self, crate_id: CrateId) -> FileId { + self.crate_roots[&crate_id] + } + pub fn add_crate_root(&mut self, file_id: FileId) -> CrateId { + let crate_id = CrateId(self.crate_roots.len() as u32); + let prev = self.crate_roots.insert(crate_id, file_id); + assert!(prev.is_none()); + crate_id + } + pub fn crate_id_for_crate_root(&self, file_id: FileId) -> Option { + let (&crate_id, _) = self + .crate_roots + .iter() + .find(|(_crate_id, &root_id)| root_id == file_id)?; + Some(crate_id) + } +} + +salsa::query_group! { + pub trait FilesDatabase: salsa::Database { + fn file_text(file_id: FileId) -> Arc { + type FileTextQuery; + storage input; + } + fn file_source_root(file_id: FileId) -> SourceRootId { + type FileSourceRootQuery; + storage input; + } + fn source_root(id: SourceRootId) -> Arc { + type SourceRootQuery; + storage input; + } + fn libraries() -> Arc> { + type LibrariesQuery; + storage input; + } + fn crate_graph() -> Arc { + type CrateGraphQuery; + storage input; + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub struct SourceRootId(pub u32); + +#[derive(Default, Clone, Debug, PartialEq, Eq)] +pub struct SourceRoot { + pub file_resolver: FileResolverImp, + pub files: FxHashSet, +} + +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..833f95eeb --- /dev/null +++ b/crates/ra_db/src/lib.rs @@ -0,0 +1,69 @@ +//! ra_db defines basic database traits. Concrete DB is defined by ra_analysis. + +extern crate ra_editor; +extern crate ra_syntax; +extern crate relative_path; +extern crate rustc_hash; +extern crate salsa; + +mod syntax_ptr; +mod file_resolver; +mod input; +mod loc2id; + +use std::sync::Arc; +use ra_editor::LineIndex; +use ra_syntax::SourceFileNode; + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub struct Canceled; + +pub type Cancelable = Result; + +impl std::fmt::Display for Canceled { + fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + fmt.write_str("Canceled") + } +} + +impl std::error::Error for Canceled {} + +pub use crate::{ + syntax_ptr::LocalSyntaxPtr, + file_resolver::{FileResolver, FileResolverImp}, + input::{ + FilesDatabase, FileId, CrateId, SourceRoot, SourceRootId, CrateGraph, WORKSPACE, + FileTextQuery, FileSourceRootQuery, SourceRootQuery, LibrariesQuery, CrateGraphQuery, + }, + loc2id::{LocationIntener, NumericId}, +}; + +pub trait BaseDatabase: salsa::Database { + fn check_canceled(&self) -> Cancelable<()> { + if self.salsa_runtime().is_current_revision_canceled() { + Err(Canceled) + } else { + Ok(()) + } + } +} + +salsa::query_group! { + pub trait SyntaxDatabase: crate::input::FilesDatabase + BaseDatabase { + fn source_file(file_id: FileId) -> SourceFileNode { + type SourceFileQuery; + } + fn file_lines(file_id: FileId) -> Arc { + type FileLinesQuery; + } + } +} + +fn source_file(db: &impl SyntaxDatabase, file_id: FileId) -> SourceFileNode { + let text = db.file_text(file_id); + SourceFileNode::parse(&*text) +} +fn file_lines(db: &impl SyntaxDatabase, file_id: FileId) -> Arc { + let text = db.file_text(file_id); + Arc::new(LineIndex::new(&*text)) +} 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 @@ +use parking_lot::Mutex; + +use std::hash::Hash; + +use rustc_hash::FxHashMap; + +/// There are two principle ways to refer to things: +/// - by their locatinon (module in foo/bar/baz.rs at line 42) +/// - by their numeric id (module `ModuleId(42)`) +/// +/// The first one is more powerful (you can actually find the thing in question +/// by id), but the second one is so much more compact. +/// +/// `Loc2IdMap` allows us to have a cake an eat it as well: by maintaining a +/// bidirectional mapping between positional and numeric ids, we can use compact +/// representation wich still allows us to get the actual item +#[derive(Debug)] +struct Loc2IdMap +where + ID: NumericId, + LOC: Clone + Eq + Hash, +{ + loc2id: FxHashMap, + id2loc: FxHashMap, +} + +impl Default for Loc2IdMap +where + ID: NumericId, + LOC: Clone + Eq + Hash, +{ + fn default() -> Self { + Loc2IdMap { + loc2id: FxHashMap::default(), + id2loc: FxHashMap::default(), + } + } +} + +impl Loc2IdMap +where + ID: NumericId, + LOC: Clone + Eq + Hash, +{ + pub fn loc2id(&mut self, loc: &LOC) -> ID { + match self.loc2id.get(loc) { + Some(id) => return id.clone(), + None => (), + } + let id = self.loc2id.len(); + assert!(id < u32::max_value() as usize); + let id = ID::from_u32(id as u32); + self.loc2id.insert(loc.clone(), id.clone()); + self.id2loc.insert(id.clone(), loc.clone()); + id + } + + pub fn id2loc(&self, id: ID) -> LOC { + self.id2loc[&id].clone() + } +} + +pub trait NumericId: Clone + Eq + Hash { + fn from_u32(id: u32) -> Self; + fn to_u32(self) -> u32; +} + +#[derive(Debug)] +pub struct LocationIntener +where + ID: NumericId, + LOC: Clone + Eq + Hash, +{ + map: Mutex>, +} + +impl Default for LocationIntener +where + ID: NumericId, + LOC: Clone + Eq + Hash, +{ + fn default() -> Self { + LocationIntener { + map: Default::default(), + } + } +} + +impl LocationIntener +where + ID: NumericId, + LOC: Clone + Eq + Hash, +{ + pub fn loc2id(&self, loc: &LOC) -> ID { + self.map.lock().loc2id(loc) + } + pub fn id2loc(&self, id: ID) -> LOC { + self.map.lock().id2loc(id) + } +} 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 @@ +use ra_syntax::{SourceFileNode, SyntaxKind, SyntaxNode, SyntaxNodeRef, TextRange}; + +/// A pionter to a syntax node inside a file. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct LocalSyntaxPtr { + range: TextRange, + kind: SyntaxKind, +} + +impl LocalSyntaxPtr { + pub fn new(node: SyntaxNodeRef) -> LocalSyntaxPtr { + LocalSyntaxPtr { + range: node.range(), + kind: node.kind(), + } + } + + pub fn resolve(self, file: &SourceFileNode) -> SyntaxNode { + let mut curr = file.syntax(); + loop { + if curr.range() == self.range && curr.kind() == self.kind { + return curr.owned(); + } + curr = curr + .children() + .find(|it| self.range.is_subrange(&it.range())) + .unwrap_or_else(|| panic!("can't resolve local ptr to SyntaxNode: {:?}", self)) + } + } + + pub fn range(self) -> TextRange { + self.range + } +} + +#[test] +fn test_local_syntax_ptr() { + use ra_syntax::{ast, AstNode}; + let file = SourceFileNode::parse("struct Foo { f: u32, }"); + let field = file + .syntax() + .descendants() + .find_map(ast::NamedFieldDef::cast) + .unwrap(); + let ptr = LocalSyntaxPtr::new(field.syntax()); + let field_syntax = ptr.resolve(&file); + assert_eq!(field.syntax(), field_syntax); +} -- cgit v1.2.3 From f66e5b6e6b6f7b2b899ef4207dfe46655d77334c Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 28 Nov 2018 03:31:50 +0300 Subject: move ids to HIR --- crates/ra_db/src/lib.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'crates/ra_db/src') diff --git a/crates/ra_db/src/lib.rs b/crates/ra_db/src/lib.rs index 833f95eeb..c5587c950 100644 --- a/crates/ra_db/src/lib.rs +++ b/crates/ra_db/src/lib.rs @@ -38,6 +38,20 @@ pub use crate::{ loc2id::{LocationIntener, NumericId}, }; +#[macro_export] +macro_rules! impl_numeric_id { + ($id:ident) => { + impl $crate::NumericId for $id { + fn from_u32(id: u32) -> Self { + $id(id) + } + fn to_u32(self) -> u32 { + self.0 + } + } + }; +} + pub trait BaseDatabase: salsa::Database { fn check_canceled(&self) -> Cancelable<()> { if self.salsa_runtime().is_current_revision_canceled() { -- cgit v1.2.3 From 0e4b710af83844f4a7c471c5335c99aaaa25a90c Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 28 Nov 2018 03:42:26 +0300 Subject: introduce hir crate --- crates/ra_db/src/lib.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'crates/ra_db/src') diff --git a/crates/ra_db/src/lib.rs b/crates/ra_db/src/lib.rs index c5587c950..33cb0e2ec 100644 --- a/crates/ra_db/src/lib.rs +++ b/crates/ra_db/src/lib.rs @@ -13,7 +13,7 @@ mod loc2id; use std::sync::Arc; use ra_editor::LineIndex; -use ra_syntax::SourceFileNode; +use ra_syntax::{TextUnit, SourceFileNode}; #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct Canceled; @@ -81,3 +81,9 @@ fn file_lines(db: &impl SyntaxDatabase, file_id: FileId) -> Arc { let text = db.file_text(file_id); Arc::new(LineIndex::new(&*text)) } + +#[derive(Clone, Copy, Debug)] +pub struct FilePosition { + pub file_id: FileId, + pub offset: TextUnit, +} -- cgit v1.2.3