diff options
Diffstat (limited to 'crates/ra_hir/src/source_id.rs')
-rw-r--r-- | crates/ra_hir/src/source_id.rs | 132 |
1 files changed, 22 insertions, 110 deletions
diff --git a/crates/ra_hir/src/source_id.rs b/crates/ra_hir/src/source_id.rs index a4dd99598..260b79661 100644 --- a/crates/ra_hir/src/source_id.rs +++ b/crates/ra_hir/src/source_id.rs | |||
@@ -2,18 +2,18 @@ | |||
2 | 2 | ||
3 | use std::{ | 3 | use std::{ |
4 | hash::{Hash, Hasher}, | 4 | hash::{Hash, Hasher}, |
5 | marker::PhantomData, | ||
6 | sync::Arc, | 5 | sync::Arc, |
7 | }; | 6 | }; |
8 | 7 | ||
9 | use ra_arena::{impl_arena_id, Arena, RawId}; | 8 | pub use hir_def::ast_id_map::{AstIdMap, ErasedFileAstId, FileAstId}; |
10 | use ra_syntax::{ast, AstNode, SyntaxNode, SyntaxNodePtr}; | 9 | use ra_syntax::{AstNode, SyntaxNode}; |
11 | 10 | ||
12 | use crate::{db::AstDatabase, HirFileId}; | 11 | use crate::{db::AstDatabase, HirFileId}; |
13 | 12 | ||
14 | /// `AstId` points to an AST node in any file. | 13 | /// `AstId` points to an AST node in any file. |
15 | /// | 14 | /// |
16 | /// It is stable across reparses, and can be used as salsa key/value. | 15 | /// It is stable across reparses, and can be used as salsa key/value. |
16 | // FIXME: isn't this just a `Source<FileAstId<N>>` ? | ||
17 | #[derive(Debug)] | 17 | #[derive(Debug)] |
18 | pub(crate) struct AstId<N: AstNode> { | 18 | pub(crate) struct AstId<N: AstNode> { |
19 | file_id: HirFileId, | 19 | file_id: HirFileId, |
@@ -40,122 +40,34 @@ impl<N: AstNode> Hash for AstId<N> { | |||
40 | } | 40 | } |
41 | 41 | ||
42 | impl<N: AstNode> AstId<N> { | 42 | impl<N: AstNode> AstId<N> { |
43 | pub fn new(file_id: HirFileId, file_ast_id: FileAstId<N>) -> AstId<N> { | ||
44 | AstId { file_id, file_ast_id } | ||
45 | } | ||
46 | |||
43 | pub(crate) fn file_id(&self) -> HirFileId { | 47 | pub(crate) fn file_id(&self) -> HirFileId { |
44 | self.file_id | 48 | self.file_id |
45 | } | 49 | } |
46 | 50 | ||
47 | pub(crate) fn to_node(&self, db: &impl AstDatabase) -> N { | 51 | pub(crate) fn to_node(&self, db: &impl AstDatabase) -> N { |
48 | let syntax_node = db.ast_id_to_node(self.file_id, self.file_ast_id.raw); | 52 | let syntax_node = db.ast_id_to_node(self.file_id, self.file_ast_id.into()); |
49 | N::cast(syntax_node).unwrap() | 53 | N::cast(syntax_node).unwrap() |
50 | } | 54 | } |
51 | } | 55 | } |
52 | 56 | ||
53 | /// `AstId` points to an AST node in a specific file. | 57 | pub(crate) fn ast_id_map_query(db: &impl AstDatabase, file_id: HirFileId) -> Arc<AstIdMap> { |
54 | #[derive(Debug)] | 58 | let map = if let Some(node) = db.parse_or_expand(file_id) { |
55 | pub(crate) struct FileAstId<N: AstNode> { | 59 | AstIdMap::from_source(&node) |
56 | raw: ErasedFileAstId, | 60 | } else { |
57 | _ty: PhantomData<fn() -> N>, | 61 | AstIdMap::default() |
58 | } | 62 | }; |
59 | 63 | Arc::new(map) | |
60 | impl<N: AstNode> Clone for FileAstId<N> { | ||
61 | fn clone(&self) -> FileAstId<N> { | ||
62 | *self | ||
63 | } | ||
64 | } | ||
65 | impl<N: AstNode> Copy for FileAstId<N> {} | ||
66 | |||
67 | impl<N: AstNode> PartialEq for FileAstId<N> { | ||
68 | fn eq(&self, other: &Self) -> bool { | ||
69 | self.raw == other.raw | ||
70 | } | ||
71 | } | ||
72 | impl<N: AstNode> Eq for FileAstId<N> {} | ||
73 | impl<N: AstNode> Hash for FileAstId<N> { | ||
74 | fn hash<H: Hasher>(&self, hasher: &mut H) { | ||
75 | self.raw.hash(hasher); | ||
76 | } | ||
77 | } | ||
78 | |||
79 | impl<N: AstNode> FileAstId<N> { | ||
80 | pub(crate) fn with_file_id(self, file_id: HirFileId) -> AstId<N> { | ||
81 | AstId { file_id, file_ast_id: self } | ||
82 | } | ||
83 | } | ||
84 | |||
85 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] | ||
86 | pub struct ErasedFileAstId(RawId); | ||
87 | impl_arena_id!(ErasedFileAstId); | ||
88 | |||
89 | /// Maps items' `SyntaxNode`s to `ErasedFileAstId`s and back. | ||
90 | #[derive(Debug, PartialEq, Eq, Default)] | ||
91 | pub struct AstIdMap { | ||
92 | arena: Arena<ErasedFileAstId, SyntaxNodePtr>, | ||
93 | } | 64 | } |
94 | 65 | ||
95 | impl AstIdMap { | 66 | pub(crate) fn file_item_query( |
96 | pub(crate) fn ast_id_map_query(db: &impl AstDatabase, file_id: HirFileId) -> Arc<AstIdMap> { | 67 | db: &impl AstDatabase, |
97 | let map = if let Some(node) = db.parse_or_expand(file_id) { | 68 | file_id: HirFileId, |
98 | AstIdMap::from_source(&node) | 69 | ast_id: ErasedFileAstId, |
99 | } else { | 70 | ) -> SyntaxNode { |
100 | AstIdMap::default() | 71 | let node = db.parse_or_expand(file_id).unwrap(); |
101 | }; | 72 | db.ast_id_map(file_id)[ast_id].to_node(&node) |
102 | Arc::new(map) | ||
103 | } | ||
104 | |||
105 | pub(crate) fn file_item_query( | ||
106 | db: &impl AstDatabase, | ||
107 | file_id: HirFileId, | ||
108 | ast_id: ErasedFileAstId, | ||
109 | ) -> SyntaxNode { | ||
110 | let node = db.parse_or_expand(file_id).unwrap(); | ||
111 | db.ast_id_map(file_id).arena[ast_id].to_node(&node) | ||
112 | } | ||
113 | |||
114 | pub(crate) fn ast_id<N: AstNode>(&self, item: &N) -> FileAstId<N> { | ||
115 | let ptr = SyntaxNodePtr::new(item.syntax()); | ||
116 | let raw = match self.arena.iter().find(|(_id, i)| **i == ptr) { | ||
117 | Some((it, _)) => it, | ||
118 | None => panic!( | ||
119 | "Can't find {:?} in AstIdMap:\n{:?}", | ||
120 | item.syntax(), | ||
121 | self.arena.iter().map(|(_id, i)| i).collect::<Vec<_>>(), | ||
122 | ), | ||
123 | }; | ||
124 | |||
125 | FileAstId { raw, _ty: PhantomData } | ||
126 | } | ||
127 | |||
128 | fn from_source(node: &SyntaxNode) -> AstIdMap { | ||
129 | assert!(node.parent().is_none()); | ||
130 | let mut res = AstIdMap { arena: Arena::default() }; | ||
131 | // By walking the tree in bread-first order we make sure that parents | ||
132 | // get lower ids then children. That is, adding a new child does not | ||
133 | // change parent's id. This means that, say, adding a new function to a | ||
134 | // trait does not change ids of top-level items, which helps caching. | ||
135 | bfs(node, |it| { | ||
136 | if let Some(module_item) = ast::ModuleItem::cast(it.clone()) { | ||
137 | res.alloc(module_item.syntax()); | ||
138 | } else if let Some(macro_call) = ast::MacroCall::cast(it) { | ||
139 | res.alloc(macro_call.syntax()); | ||
140 | } | ||
141 | }); | ||
142 | res | ||
143 | } | ||
144 | |||
145 | fn alloc(&mut self, item: &SyntaxNode) -> ErasedFileAstId { | ||
146 | self.arena.alloc(SyntaxNodePtr::new(item)) | ||
147 | } | ||
148 | } | ||
149 | |||
150 | /// Walks the subtree in bfs order, calling `f` for each node. | ||
151 | fn bfs(node: &SyntaxNode, mut f: impl FnMut(SyntaxNode)) { | ||
152 | let mut curr_layer = vec![node.clone()]; | ||
153 | let mut next_layer = vec![]; | ||
154 | while !curr_layer.is_empty() { | ||
155 | curr_layer.drain(..).for_each(|node| { | ||
156 | next_layer.extend(node.children()); | ||
157 | f(node); | ||
158 | }); | ||
159 | std::mem::swap(&mut curr_layer, &mut next_layer); | ||
160 | } | ||
161 | } | 73 | } |