From 0b820cacab020993b6e1667f491289122f03de04 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Tue, 26 Mar 2019 14:40:34 +0300 Subject: move source_id to a separate file --- crates/ra_hir/src/source_id.rs | 113 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 crates/ra_hir/src/source_id.rs (limited to 'crates/ra_hir/src/source_id.rs') diff --git a/crates/ra_hir/src/source_id.rs b/crates/ra_hir/src/source_id.rs new file mode 100644 index 000000000..f961adf8b --- /dev/null +++ b/crates/ra_hir/src/source_id.rs @@ -0,0 +1,113 @@ +use std::sync::Arc; + +use ra_arena::{Arena, RawId, impl_arena_id}; +use ra_syntax::{SyntaxNodePtr, TreeArc, SyntaxNode, SourceFile, AstNode, ast}; + +use crate::{HirFileId, DefDatabase}; + +/// Identifier of item within a specific file. This is stable over reparses, so +/// it's OK to use it as a salsa key/value. +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct SourceFileItemId(RawId); +impl_arena_id!(SourceFileItemId); + +impl SourceFileItemId { + pub(crate) fn with_file_id(self, file_id: HirFileId) -> SourceItemId { + SourceItemId { file_id, item_id: self } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct SourceItemId { + pub(crate) file_id: HirFileId, + pub(crate) item_id: SourceFileItemId, +} + +/// Maps items' `SyntaxNode`s to `SourceFileItemId`s and back. +#[derive(Debug, PartialEq, Eq)] +pub struct SourceFileItems { + file_id: HirFileId, + arena: Arena, +} + +impl SourceFileItems { + pub(crate) fn file_items_query( + db: &impl DefDatabase, + file_id: HirFileId, + ) -> Arc { + let source_file = db.hir_parse(file_id); + Arc::new(SourceFileItems::from_source_file(&source_file, file_id)) + } + + pub(crate) fn file_item_query( + db: &impl DefDatabase, + source_item_id: SourceItemId, + ) -> TreeArc { + let source_file = db.hir_parse(source_item_id.file_id); + db.file_items(source_item_id.file_id)[source_item_id.item_id] + .to_node(&source_file) + .to_owned() + } + + pub(crate) fn from_source_file( + source_file: &SourceFile, + file_id: HirFileId, + ) -> SourceFileItems { + let mut res = SourceFileItems { file_id, arena: Arena::default() }; + // By walking the tree in bread-first order we make sure that parents + // get lower ids then children. That is, adding a new child does not + // change parent's id. This means that, say, adding a new function to a + // trait does not change ids of top-level items, which helps caching. + bfs(source_file.syntax(), |it| { + if let Some(module_item) = ast::ModuleItem::cast(it) { + res.alloc(module_item.syntax()); + } else if let Some(macro_call) = ast::MacroCall::cast(it) { + res.alloc(macro_call.syntax()); + } + }); + res + } + + fn alloc(&mut self, item: &SyntaxNode) -> SourceFileItemId { + self.arena.alloc(SyntaxNodePtr::new(item)) + } + pub(crate) fn id_of(&self, file_id: HirFileId, item: &SyntaxNode) -> SourceFileItemId { + assert_eq!( + self.file_id, file_id, + "SourceFileItems: wrong file, expected {:?}, got {:?}", + self.file_id, file_id + ); + self.id_of_unchecked(item) + } + pub(crate) fn id_of_unchecked(&self, item: &SyntaxNode) -> SourceFileItemId { + let ptr = SyntaxNodePtr::new(item); + if let Some((id, _)) = self.arena.iter().find(|(_id, i)| **i == ptr) { + return id; + } + panic!( + "Can't find {:?} in SourceFileItems:\n{:?}", + item, + self.arena.iter().map(|(_id, i)| i).collect::>(), + ); + } +} + +impl std::ops::Index for SourceFileItems { + type Output = SyntaxNodePtr; + fn index(&self, idx: SourceFileItemId) -> &SyntaxNodePtr { + &self.arena[idx] + } +} + +/// Walks the subtree in bfs order, calling `f` for each node. +fn bfs(node: &SyntaxNode, mut f: impl FnMut(&SyntaxNode)) { + let mut curr_layer = vec![node]; + let mut next_layer = vec![]; + while !curr_layer.is_empty() { + curr_layer.drain(..).for_each(|node| { + next_layer.extend(node.children()); + f(node); + }); + std::mem::swap(&mut curr_layer, &mut next_layer); + } +} -- cgit v1.2.3