move db basics to ra_db

This should allow to move hir to a separate crate
This commit is contained in:
Aleksey Kladov 2018-11-28 03:25:20 +03:00
parent b2de95879a
commit 11168c464c
22 changed files with 352 additions and 293 deletions

View file

@ -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<FileId>;
fn debug_path(&self, _1file_id: FileId) -> Option<std::path::PathBuf> {
None
}
}
#[derive(Clone, Debug)]
pub struct FileResolverImp {
inner: Arc<FileResolver>,
}
impl PartialEq for FileResolverImp {
fn eq(&self, other: &FileResolverImp) -> bool {
self.inner() == other.inner()
}
}
impl Eq for FileResolverImp {}
impl Hash for FileResolverImp {
fn hash<H: Hasher>(&self, hasher: &mut H) {
self.inner().hash(hasher);
}
}
impl FileResolverImp {
pub fn new(inner: Arc<FileResolver>) -> 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<FileId> {
self.inner.resolve(file_id, path)
}
pub fn debug_path(&self, file_id: FileId) -> Option<std::path::PathBuf> {
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<FileId> {
panic!("file resolver not set")
}
}
FileResolverImp {
inner: Arc::new(DummyResolver),
}
}
}

73
crates/ra_db/src/input.rs Normal file
View file

@ -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<CrateId, FileId>,
}
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<CrateId> {
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<String> {
type FileTextQuery;
storage input;
}
fn file_source_root(file_id: FileId) -> SourceRootId {
type FileSourceRootQuery;
storage input;
}
fn source_root(id: SourceRootId) -> Arc<SourceRoot> {
type SourceRootQuery;
storage input;
}
fn libraries() -> Arc<Vec<SourceRootId>> {
type LibrariesQuery;
storage input;
}
fn crate_graph() -> Arc<CrateGraph> {
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<FileId>,
}
pub const WORKSPACE: SourceRootId = SourceRootId(0);

69
crates/ra_db/src/lib.rs Normal file
View file

@ -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<T> = Result<T, Canceled>;
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<LineIndex> {
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<LineIndex> {
let text = db.file_text(file_id);
Arc::new(LineIndex::new(&*text))
}

100
crates/ra_db/src/loc2id.rs Normal file
View file

@ -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<LOC, ID>
where
ID: NumericId,
LOC: Clone + Eq + Hash,
{
loc2id: FxHashMap<LOC, ID>,
id2loc: FxHashMap<ID, LOC>,
}
impl<LOC, ID> Default for Loc2IdMap<LOC, ID>
where
ID: NumericId,
LOC: Clone + Eq + Hash,
{
fn default() -> Self {
Loc2IdMap {
loc2id: FxHashMap::default(),
id2loc: FxHashMap::default(),
}
}
}
impl<LOC, ID> Loc2IdMap<LOC, ID>
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<LOC, ID>
where
ID: NumericId,
LOC: Clone + Eq + Hash,
{
map: Mutex<Loc2IdMap<LOC, ID>>,
}
impl<LOC, ID> Default for LocationIntener<LOC, ID>
where
ID: NumericId,
LOC: Clone + Eq + Hash,
{
fn default() -> Self {
LocationIntener {
map: Default::default(),
}
}
}
impl<LOC, ID> LocationIntener<LOC, ID>
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)
}
}

View file

@ -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);
}