diff options
Diffstat (limited to 'crates/ra_hir/src/source_id.rs')
-rw-r--r-- | crates/ra_hir/src/source_id.rs | 157 |
1 files changed, 97 insertions, 60 deletions
diff --git a/crates/ra_hir/src/source_id.rs b/crates/ra_hir/src/source_id.rs index 62707ba6a..0a8fb6d32 100644 --- a/crates/ra_hir/src/source_id.rs +++ b/crates/ra_hir/src/source_id.rs | |||
@@ -1,59 +1,122 @@ | |||
1 | use std::sync::Arc; | 1 | use std::{marker::PhantomData, sync::Arc, hash::{Hash, Hasher}}; |
2 | 2 | ||
3 | use ra_arena::{Arena, RawId, impl_arena_id}; | 3 | use ra_arena::{Arena, RawId, impl_arena_id}; |
4 | use ra_syntax::{SyntaxNodePtr, TreeArc, SyntaxNode, SourceFile, AstNode, ast}; | 4 | use ra_syntax::{SyntaxNodePtr, TreeArc, SyntaxNode, SourceFile, AstNode, ast}; |
5 | 5 | ||
6 | use crate::{HirFileId, DefDatabase}; | 6 | use crate::{HirFileId, DefDatabase}; |
7 | 7 | ||
8 | /// Identifier of item within a specific file. This is stable over reparses, so | 8 | /// `AstId` points to an AST node in any file. |
9 | /// it's OK to use it as a salsa key/value. | 9 | /// |
10 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] | 10 | /// It is stable across reparses, and can be used as salsa key/value. |
11 | pub(crate) struct SourceFileItemId(RawId); | 11 | #[derive(Debug)] |
12 | impl_arena_id!(SourceFileItemId); | 12 | pub(crate) struct AstId<N: AstNode> { |
13 | file_id: HirFileId, | ||
14 | file_ast_id: FileAstId<N>, | ||
15 | } | ||
16 | |||
17 | impl<N: AstNode> Clone for AstId<N> { | ||
18 | fn clone(&self) -> AstId<N> { | ||
19 | *self | ||
20 | } | ||
21 | } | ||
22 | impl<N: AstNode> Copy for AstId<N> {} | ||
23 | |||
24 | impl<N: AstNode> PartialEq for AstId<N> { | ||
25 | fn eq(&self, other: &Self) -> bool { | ||
26 | (self.file_id, self.file_ast_id) == (other.file_id, other.file_ast_id) | ||
27 | } | ||
28 | } | ||
29 | impl<N: AstNode> Eq for AstId<N> {} | ||
30 | impl<N: AstNode> Hash for AstId<N> { | ||
31 | fn hash<H: Hasher>(&self, hasher: &mut H) { | ||
32 | (self.file_id, self.file_ast_id).hash(hasher); | ||
33 | } | ||
34 | } | ||
35 | |||
36 | impl<N: AstNode> AstId<N> { | ||
37 | pub(crate) fn file_id(&self) -> HirFileId { | ||
38 | self.file_id | ||
39 | } | ||
40 | |||
41 | pub(crate) fn to_node(&self, db: &impl DefDatabase) -> TreeArc<N> { | ||
42 | let syntax_node = db.ast_id_to_node(self.file_id, self.file_ast_id.raw); | ||
43 | N::cast(&syntax_node).unwrap().to_owned() | ||
44 | } | ||
45 | } | ||
46 | |||
47 | /// `AstId` points to an AST node in a specific file. | ||
48 | #[derive(Debug)] | ||
49 | pub(crate) struct FileAstId<N: AstNode> { | ||
50 | raw: ErasedFileAstId, | ||
51 | _ty: PhantomData<N>, | ||
52 | } | ||
53 | |||
54 | impl<N: AstNode> Clone for FileAstId<N> { | ||
55 | fn clone(&self) -> FileAstId<N> { | ||
56 | *self | ||
57 | } | ||
58 | } | ||
59 | impl<N: AstNode> Copy for FileAstId<N> {} | ||
13 | 60 | ||
14 | impl SourceFileItemId { | 61 | impl<N: AstNode> PartialEq for FileAstId<N> { |
15 | pub(crate) fn with_file_id(self, file_id: HirFileId) -> SourceItemId { | 62 | fn eq(&self, other: &Self) -> bool { |
16 | SourceItemId { file_id, item_id: self } | 63 | self.raw == other.raw |
64 | } | ||
65 | } | ||
66 | impl<N: AstNode> Eq for FileAstId<N> {} | ||
67 | impl<N: AstNode> Hash for FileAstId<N> { | ||
68 | fn hash<H: Hasher>(&self, hasher: &mut H) { | ||
69 | self.raw.hash(hasher); | ||
17 | } | 70 | } |
18 | } | 71 | } |
19 | 72 | ||
20 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 73 | impl<N: AstNode> FileAstId<N> { |
21 | pub struct SourceItemId { | 74 | pub(crate) fn with_file_id(self, file_id: HirFileId) -> AstId<N> { |
22 | pub(crate) file_id: HirFileId, | 75 | AstId { file_id, file_ast_id: self } |
23 | pub(crate) item_id: SourceFileItemId, | 76 | } |
24 | } | 77 | } |
25 | 78 | ||
26 | /// Maps items' `SyntaxNode`s to `SourceFileItemId`s and back. | 79 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] |
80 | pub struct ErasedFileAstId(RawId); | ||
81 | impl_arena_id!(ErasedFileAstId); | ||
82 | |||
83 | /// Maps items' `SyntaxNode`s to `ErasedFileAstId`s and back. | ||
27 | #[derive(Debug, PartialEq, Eq)] | 84 | #[derive(Debug, PartialEq, Eq)] |
28 | pub struct SourceFileItems { | 85 | pub struct AstIdMap { |
29 | file_id: HirFileId, | 86 | arena: Arena<ErasedFileAstId, SyntaxNodePtr>, |
30 | arena: Arena<SourceFileItemId, SyntaxNodePtr>, | ||
31 | } | 87 | } |
32 | 88 | ||
33 | impl SourceFileItems { | 89 | impl AstIdMap { |
34 | pub(crate) fn file_items_query( | 90 | pub(crate) fn ast_id_map_query(db: &impl DefDatabase, file_id: HirFileId) -> Arc<AstIdMap> { |
35 | db: &impl DefDatabase, | ||
36 | file_id: HirFileId, | ||
37 | ) -> Arc<SourceFileItems> { | ||
38 | let source_file = db.hir_parse(file_id); | 91 | let source_file = db.hir_parse(file_id); |
39 | Arc::new(SourceFileItems::from_source_file(&source_file, file_id)) | 92 | Arc::new(AstIdMap::from_source_file(&source_file)) |
40 | } | 93 | } |
41 | 94 | ||
42 | pub(crate) fn file_item_query( | 95 | pub(crate) fn file_item_query( |
43 | db: &impl DefDatabase, | 96 | db: &impl DefDatabase, |
44 | source_item_id: SourceItemId, | 97 | file_id: HirFileId, |
98 | ast_id: ErasedFileAstId, | ||
45 | ) -> TreeArc<SyntaxNode> { | 99 | ) -> TreeArc<SyntaxNode> { |
46 | let source_file = db.hir_parse(source_item_id.file_id); | 100 | let source_file = db.hir_parse(file_id); |
47 | db.file_items(source_item_id.file_id)[source_item_id.item_id] | 101 | db.ast_id_map(file_id).arena[ast_id].to_node(&source_file).to_owned() |
48 | .to_node(&source_file) | ||
49 | .to_owned() | ||
50 | } | 102 | } |
51 | 103 | ||
52 | pub(crate) fn from_source_file( | 104 | pub(crate) fn ast_id<N: AstNode>(&self, item: &N) -> FileAstId<N> { |
53 | source_file: &SourceFile, | 105 | let ptr = SyntaxNodePtr::new(item.syntax()); |
54 | file_id: HirFileId, | 106 | let raw = match self.arena.iter().find(|(_id, i)| **i == ptr) { |
55 | ) -> SourceFileItems { | 107 | Some((it, _)) => it, |
56 | let mut res = SourceFileItems { file_id, arena: Arena::default() }; | 108 | None => panic!( |
109 | "Can't find {:?} in AstIdMap:\n{:?}", | ||
110 | item.syntax(), | ||
111 | self.arena.iter().map(|(_id, i)| i).collect::<Vec<_>>(), | ||
112 | ), | ||
113 | }; | ||
114 | |||
115 | FileAstId { raw, _ty: PhantomData } | ||
116 | } | ||
117 | |||
118 | fn from_source_file(source_file: &SourceFile) -> AstIdMap { | ||
119 | let mut res = AstIdMap { arena: Arena::default() }; | ||
57 | // By walking the tree in bread-first order we make sure that parents | 120 | // By walking the tree in bread-first order we make sure that parents |
58 | // get lower ids then children. That is, adding a new child does not | 121 | // get lower ids then children. That is, adding a new child does not |
59 | // change parent's id. This means that, say, adding a new function to a | 122 | // change parent's id. This means that, say, adding a new function to a |
@@ -68,35 +131,9 @@ impl SourceFileItems { | |||
68 | res | 131 | res |
69 | } | 132 | } |
70 | 133 | ||
71 | fn alloc(&mut self, item: &SyntaxNode) -> SourceFileItemId { | 134 | fn alloc(&mut self, item: &SyntaxNode) -> ErasedFileAstId { |
72 | self.arena.alloc(SyntaxNodePtr::new(item)) | 135 | self.arena.alloc(SyntaxNodePtr::new(item)) |
73 | } | 136 | } |
74 | pub(crate) fn id_of(&self, file_id: HirFileId, item: &SyntaxNode) -> SourceFileItemId { | ||
75 | assert_eq!( | ||
76 | self.file_id, file_id, | ||
77 | "SourceFileItems: wrong file, expected {:?}, got {:?}", | ||
78 | self.file_id, file_id | ||
79 | ); | ||
80 | self.id_of_unchecked(item) | ||
81 | } | ||
82 | pub(crate) fn id_of_unchecked(&self, item: &SyntaxNode) -> SourceFileItemId { | ||
83 | let ptr = SyntaxNodePtr::new(item); | ||
84 | if let Some((id, _)) = self.arena.iter().find(|(_id, i)| **i == ptr) { | ||
85 | return id; | ||
86 | } | ||
87 | panic!( | ||
88 | "Can't find {:?} in SourceFileItems:\n{:?}", | ||
89 | item, | ||
90 | self.arena.iter().map(|(_id, i)| i).collect::<Vec<_>>(), | ||
91 | ); | ||
92 | } | ||
93 | } | ||
94 | |||
95 | impl std::ops::Index<SourceFileItemId> for SourceFileItems { | ||
96 | type Output = SyntaxNodePtr; | ||
97 | fn index(&self, idx: SourceFileItemId) -> &SyntaxNodePtr { | ||
98 | &self.arena[idx] | ||
99 | } | ||
100 | } | 137 | } |
101 | 138 | ||
102 | /// Walks the subtree in bfs order, calling `f` for each node. | 139 | /// Walks the subtree in bfs order, calling `f` for each node. |