mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-01 22:31:43 +00:00
move db basics to ra_db
This should allow to move hir to a separate crate
This commit is contained in:
parent
b2de95879a
commit
11168c464c
22 changed files with 352 additions and 293 deletions
76
crates/ra_db/src/file_resolver.rs
Normal file
76
crates/ra_db/src/file_resolver.rs
Normal 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
73
crates/ra_db/src/input.rs
Normal 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
69
crates/ra_db/src/lib.rs
Normal 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
100
crates/ra_db/src/loc2id.rs
Normal 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)
|
||||
}
|
||||
}
|
48
crates/ra_db/src/syntax_ptr.rs
Normal file
48
crates/ra_db/src/syntax_ptr.rs
Normal 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);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue