4
4
#[ cfg( test) ]
5
5
mod tests;
6
6
7
- use crate :: qsc_utils:: { map_offset, span_contains, Compilation } ;
8
- use qsc:: hir :: { visit:: walk_expr , visit :: Visitor , Expr , ExprKind , ItemKind , Package , Res } ;
7
+ use crate :: qsc_utils:: { find_item , map_offset, span_contains, Compilation } ;
8
+ use qsc:: ast :: visit:: { walk_callable_decl , walk_expr , walk_pat , walk_ty_def , Visitor } ;
9
9
use qsc:: SourceMap ;
10
+ use qsc:: { ast, hir, resolve} ;
10
11
11
12
#[ derive( Debug , PartialEq ) ]
12
13
pub struct Definition {
@@ -21,15 +22,16 @@ pub(crate) fn get_definition(
21
22
) -> Option < Definition > {
22
23
// Map the file offset into a SourceMap offset
23
24
let offset = map_offset ( & compilation. unit . sources , source_name, offset) ;
24
- let package = & compilation. unit . package ;
25
+ let ast_package = & compilation. unit . ast ;
25
26
26
27
let mut definition_finder = DefinitionFinder {
27
- package ,
28
+ compilation ,
28
29
source_map : & compilation. unit . sources ,
29
30
offset,
30
31
definition : None ,
32
+ curr_callable : None ,
31
33
} ;
32
- definition_finder. visit_package ( package) ;
34
+ definition_finder. visit_package ( & ast_package . package ) ;
33
35
34
36
definition_finder
35
37
. definition
@@ -40,43 +42,192 @@ pub(crate) fn get_definition(
40
42
}
41
43
42
44
struct DefinitionFinder < ' a > {
43
- package : & ' a Package ,
45
+ compilation : & ' a Compilation ,
44
46
source_map : & ' a SourceMap ,
45
47
offset : u32 ,
46
48
definition : Option < ( String , u32 ) > ,
49
+ curr_callable : Option < & ' a ast:: CallableDecl > ,
47
50
}
48
51
49
- impl < ' a > Visitor < ' _ > for DefinitionFinder < ' a > {
50
- fn visit_expr ( & mut self , expr : & Expr ) {
52
+ impl DefinitionFinder < ' _ > {
53
+ fn set_definition_from_position ( & mut self , lo : u32 ) {
54
+ self . definition = Some ( (
55
+ self . source_map
56
+ . find_by_offset ( lo)
57
+ . expect ( "source should exist for offset" )
58
+ . name
59
+ . to_string ( ) ,
60
+ lo,
61
+ ) ) ;
62
+ }
63
+ }
64
+
65
+ impl < ' a > Visitor < ' a > for DefinitionFinder < ' a > {
66
+ // Handles callable and UDT definitions
67
+ fn visit_item ( & mut self , item : & ' a ast:: Item ) {
68
+ if span_contains ( item. span , self . offset ) {
69
+ match & * item. kind {
70
+ ast:: ItemKind :: Callable ( decl) => {
71
+ if span_contains ( decl. name . span , self . offset ) {
72
+ self . set_definition_from_position ( decl. name . span . lo ) ;
73
+ } else if span_contains ( decl. span , self . offset ) {
74
+ self . curr_callable = Some ( decl) ;
75
+ walk_callable_decl ( self , decl) ;
76
+ self . curr_callable = None ;
77
+ }
78
+ // Note: the `item.span` can cover things like doc
79
+ // comment, attributes, and visibility keywords, which aren't
80
+ // things we want to have hover logic for, while the `decl.span` is
81
+ // specific to the contents of the callable decl, which we do want
82
+ // hover logic for. If the `if` or `else if` above is not met, then
83
+ // the user is hovering over one of these non-decl parts of the item,
84
+ // and we want to do nothing.
85
+ }
86
+ ast:: ItemKind :: Ty ( ident, def) => {
87
+ if span_contains ( ident. span , self . offset ) {
88
+ self . set_definition_from_position ( ident. span . lo ) ;
89
+ } else {
90
+ self . visit_ty_def ( def) ;
91
+ }
92
+ }
93
+ _ => { }
94
+ }
95
+ }
96
+ }
97
+
98
+ // Handles UDT field definitions
99
+ fn visit_ty_def ( & mut self , def : & ' a ast:: TyDef ) {
100
+ if span_contains ( def. span , self . offset ) {
101
+ if let ast:: TyDefKind :: Field ( ident, ty) = & * def. kind {
102
+ if let Some ( ident) = ident {
103
+ if span_contains ( ident. span , self . offset ) {
104
+ self . set_definition_from_position ( ident. span . lo ) ;
105
+ } else {
106
+ self . visit_ty ( ty) ;
107
+ }
108
+ } else {
109
+ self . visit_ty ( ty) ;
110
+ }
111
+ } else {
112
+ walk_ty_def ( self , def) ;
113
+ }
114
+ }
115
+ }
116
+
117
+ // Handles local variable definitions
118
+ fn visit_pat ( & mut self , pat : & ' a ast:: Pat ) {
119
+ if span_contains ( pat. span , self . offset ) {
120
+ match & * pat. kind {
121
+ ast:: PatKind :: Bind ( ident, anno) => {
122
+ if span_contains ( ident. span , self . offset ) {
123
+ self . set_definition_from_position ( ident. span . lo ) ;
124
+ } else if let Some ( ty) = anno {
125
+ self . visit_ty ( ty) ;
126
+ }
127
+ }
128
+ _ => walk_pat ( self , pat) ,
129
+ }
130
+ }
131
+ }
132
+
133
+ // Handles UDT field references
134
+ fn visit_expr ( & mut self , expr : & ' a ast:: Expr ) {
51
135
if span_contains ( expr. span , self . offset ) {
52
- if let ExprKind :: Var ( res, _) = expr. kind {
53
- let item = match res {
54
- Res :: Err => None ,
55
- // Just one package plus std for now, so let's live with this hack
56
- Res :: Item ( item) => {
57
- if item. package . is_none ( ) {
58
- self . package . items . get ( item. item )
59
- } else {
60
- // Handling std library is tricky for now
61
- None
136
+ match & * expr. kind {
137
+ ast:: ExprKind :: Field ( udt, field) if span_contains ( field. span , self . offset ) => {
138
+ if let Some ( hir:: ty:: Ty :: Udt ( res) ) =
139
+ self . compilation . unit . ast . tys . terms . get ( udt. id )
140
+ {
141
+ match res {
142
+ hir:: Res :: Item ( item_id) => {
143
+ if let ( Some ( item) , _) = find_item ( self . compilation , item_id) {
144
+ match & item. kind {
145
+ hir:: ItemKind :: Ty ( _, udt) => {
146
+ if let Some ( field) = udt. find_field_by_name ( & field. name )
147
+ {
148
+ let span = field. name_span . expect (
149
+ "field found via name should have a name" ,
150
+ ) ;
151
+ self . set_definition_from_position ( span. lo ) ;
152
+ }
153
+ }
154
+ _ => panic ! ( "UDT has invalid resolution." ) ,
155
+ }
156
+ }
157
+ }
158
+ _ => panic ! ( "UDT has invalid resolution." ) ,
62
159
}
63
160
}
64
- Res :: Local ( _node) => None ,
65
- } ;
66
- if let Some ( def) = item {
67
- if let ItemKind :: Callable ( decl) = & def. kind {
68
- self . definition = Some ( (
69
- self . source_map
70
- . find_by_offset ( decl. name . span . lo )
71
- . expect ( "source should exist for offset" )
72
- . name
73
- . to_string ( ) ,
74
- decl. name . span . lo ,
75
- ) ) ;
161
+ }
162
+ _ => walk_expr ( self , expr) ,
163
+ }
164
+ }
165
+ }
166
+
167
+ // Handles local variable, UDT, and callable references
168
+ fn visit_path ( & mut self , path : & ' _ ast:: Path ) {
169
+ if span_contains ( path. span , self . offset ) {
170
+ let res = self . compilation . unit . ast . names . get ( path. id ) ;
171
+ if let Some ( res) = res {
172
+ match & res {
173
+ resolve:: Res :: Item ( item_id) => {
174
+ if item_id. package . is_none ( ) {
175
+ if let ( Some ( item) , _) = find_item ( self . compilation , item_id) {
176
+ let lo = match & item. kind {
177
+ hir:: ItemKind :: Callable ( decl) => decl. name . span . lo ,
178
+ hir:: ItemKind :: Namespace ( _, _) => {
179
+ panic ! (
180
+ "Reference node should not refer to a namespace: {}" ,
181
+ path. id
182
+ )
183
+ }
184
+ hir:: ItemKind :: Ty ( ident, _) => ident. span . lo ,
185
+ } ;
186
+ self . set_definition_from_position ( lo) ;
187
+ } ;
188
+ }
76
189
}
190
+ resolve:: Res :: Local ( node_id) => {
191
+ if let Some ( curr) = self . curr_callable {
192
+ {
193
+ let mut finder = AstPatFinder {
194
+ node_id,
195
+ result : None ,
196
+ } ;
197
+ finder. visit_callable_decl ( curr) ;
198
+ if let Some ( lo) = finder. result {
199
+ self . set_definition_from_position ( lo) ;
200
+ }
201
+ }
202
+ }
203
+ }
204
+ _ => { }
77
205
}
78
206
}
79
207
}
80
- walk_expr ( self , expr) ;
208
+ }
209
+ }
210
+
211
+ struct AstPatFinder < ' a > {
212
+ node_id : & ' a ast:: NodeId ,
213
+ result : Option < u32 > ,
214
+ }
215
+
216
+ impl < ' a > Visitor < ' a > for AstPatFinder < ' _ > {
217
+ fn visit_pat ( & mut self , pat : & ' a ast:: Pat ) {
218
+ match & * pat. kind {
219
+ ast:: PatKind :: Bind ( ident, _) => {
220
+ if ident. id == * self . node_id {
221
+ self . result = Some ( ident. span . lo ) ;
222
+ }
223
+ }
224
+ _ => walk_pat ( self , pat) ,
225
+ }
226
+ }
227
+
228
+ fn visit_expr ( & mut self , expr : & ' a ast:: Expr ) {
229
+ if self . result . is_none ( ) {
230
+ walk_expr ( self , expr) ;
231
+ }
81
232
}
82
233
}
0 commit comments