@@ -4,19 +4,17 @@ mod goto;
4
4
mod hover;
5
5
mod markup;
6
6
7
- use std:: ops:: { Deref , DerefMut } ;
8
-
9
7
pub use db:: Db ;
10
8
pub use goto:: goto_type_definition;
11
9
pub use hover:: hover;
12
10
pub use markup:: MarkupKind ;
13
- use red_knot_python_semantic:: types:: {
14
- Class , ClassBase , ClassLiteralType , FunctionType , InstanceType , IntersectionType ,
15
- KnownInstanceType , ModuleLiteralType , Type ,
16
- } ;
11
+
12
+ use rustc_hash:: FxHashSet ;
13
+ use std:: ops:: { Deref , DerefMut } ;
14
+
15
+ use red_knot_python_semantic:: types:: { Type , TypeDefinition } ;
17
16
use ruff_db:: files:: { File , FileRange } ;
18
- use ruff_db:: source:: source_text;
19
- use ruff_text_size:: { Ranged , TextLen , TextRange } ;
17
+ use ruff_text_size:: { Ranged , TextRange } ;
20
18
21
19
/// Information associated with a text range.
22
20
#[ derive( Debug , Copy , Clone , Eq , PartialEq , Hash ) ]
58
56
}
59
57
60
58
/// Target to which the editor can navigate to.
61
- #[ derive( Debug , Clone ) ]
59
+ #[ derive( Debug , Clone , PartialEq , Eq , Hash ) ]
62
60
pub struct NavigationTarget {
63
61
file : File ,
64
62
@@ -99,6 +97,17 @@ impl NavigationTargets {
99
97
Self ( smallvec:: SmallVec :: new ( ) )
100
98
}
101
99
100
+ fn unique ( targets : impl IntoIterator < Item = NavigationTarget > ) -> Self {
101
+ let unique: FxHashSet < _ > = targets. into_iter ( ) . collect ( ) ;
102
+ if unique. is_empty ( ) {
103
+ Self :: empty ( )
104
+ } else {
105
+ let mut targets = unique. into_iter ( ) . collect :: < Vec < _ > > ( ) ;
106
+ targets. sort_by_key ( |target| ( target. file , target. focus_range . start ( ) ) ) ;
107
+ Self ( targets. into ( ) )
108
+ }
109
+ }
110
+
102
111
fn iter ( & self ) -> std:: slice:: Iter < ' _ , NavigationTarget > {
103
112
self . 0 . iter ( )
104
113
}
@@ -129,7 +138,7 @@ impl<'a> IntoIterator for &'a NavigationTargets {
129
138
130
139
impl FromIterator < NavigationTarget > for NavigationTargets {
131
140
fn from_iter < T : IntoIterator < Item = NavigationTarget > > ( iter : T ) -> Self {
132
- Self ( iter. into_iter ( ) . collect ( ) )
141
+ Self :: unique ( iter)
133
142
}
134
143
}
135
144
@@ -140,143 +149,50 @@ pub trait HasNavigationTargets {
140
149
impl HasNavigationTargets for Type < ' _ > {
141
150
fn navigation_targets ( & self , db : & dyn Db ) -> NavigationTargets {
142
151
match self {
143
- Type :: BoundMethod ( method) => method. function ( db) . navigation_targets ( db) ,
144
- Type :: FunctionLiteral ( function) => function. navigation_targets ( db) ,
145
- Type :: ModuleLiteral ( module) => module. navigation_targets ( db) ,
146
152
Type :: Union ( union) => union
147
153
. iter ( db. upcast ( ) )
148
154
. flat_map ( |target| target. navigation_targets ( db) )
149
155
. collect ( ) ,
150
- Type :: ClassLiteral ( class) => class. navigation_targets ( db) ,
151
- Type :: Instance ( instance) => instance. navigation_targets ( db) ,
152
- Type :: KnownInstance ( instance) => instance. navigation_targets ( db) ,
153
- Type :: SubclassOf ( subclass_of_type) => match subclass_of_type. subclass_of ( ) {
154
- ClassBase :: Class ( class) => class. navigation_targets ( db) ,
155
- ClassBase :: Dynamic ( _) => NavigationTargets :: empty ( ) ,
156
- } ,
157
156
158
- Type :: StringLiteral ( _)
159
- | Type :: BooleanLiteral ( _)
160
- | Type :: LiteralString
161
- | Type :: IntLiteral ( _)
162
- | Type :: BytesLiteral ( _)
163
- | Type :: SliceLiteral ( _)
164
- | Type :: MethodWrapper ( _)
165
- | Type :: WrapperDescriptor ( _)
166
- | Type :: PropertyInstance ( _)
167
- | Type :: Tuple ( _) => self . to_meta_type ( db. upcast ( ) ) . navigation_targets ( db) ,
168
-
169
- Type :: TypeVar ( var) => {
170
- let definition = var. definition ( db) ;
171
- let full_range = definition. full_range ( db. upcast ( ) ) ;
172
-
173
- NavigationTargets :: single ( NavigationTarget {
174
- file : full_range. file ( ) ,
175
- focus_range : definition. focus_range ( db. upcast ( ) ) . range ( ) ,
176
- full_range : full_range. range ( ) ,
177
- } )
157
+ Type :: Intersection ( intersection) => {
158
+ // Only consider the positive elements because the negative elements are mainly from narrowing constraints.
159
+ let mut targets = intersection
160
+ . iter_positive ( db. upcast ( ) )
161
+ . filter ( |ty| !ty. is_unknown ( ) ) ;
162
+
163
+ let Some ( first) = targets. next ( ) else {
164
+ return NavigationTargets :: empty ( ) ;
165
+ } ;
166
+
167
+ match targets. next ( ) {
168
+ Some ( _) => {
169
+ // If there are multiple types in the intersection, we can't navigate to a single one
170
+ // because the type is the intersection of all those types.
171
+ NavigationTargets :: empty ( )
172
+ }
173
+ None => first. navigation_targets ( db) ,
174
+ }
178
175
}
179
176
180
- Type :: Intersection ( intersection) => intersection. navigation_targets ( db) ,
181
-
182
- Type :: Dynamic ( _)
183
- | Type :: Never
184
- | Type :: Callable ( _)
185
- | Type :: AlwaysTruthy
186
- | Type :: AlwaysFalsy => NavigationTargets :: empty ( ) ,
177
+ ty => ty
178
+ . definition ( db. upcast ( ) )
179
+ . map ( |definition| definition. navigation_targets ( db) )
180
+ . unwrap_or_else ( NavigationTargets :: empty) ,
187
181
}
188
182
}
189
183
}
190
184
191
- impl HasNavigationTargets for FunctionType < ' _ > {
192
- fn navigation_targets ( & self , db : & dyn Db ) -> NavigationTargets {
193
- let function_range = self . focus_range ( db. upcast ( ) ) ;
194
- NavigationTargets :: single ( NavigationTarget {
195
- file : function_range. file ( ) ,
196
- focus_range : function_range. range ( ) ,
197
- full_range : self . full_range ( db. upcast ( ) ) . range ( ) ,
198
- } )
199
- }
200
- }
201
-
202
- impl HasNavigationTargets for Class < ' _ > {
185
+ impl HasNavigationTargets for TypeDefinition < ' _ > {
203
186
fn navigation_targets ( & self , db : & dyn Db ) -> NavigationTargets {
204
- let class_range = self . focus_range ( db. upcast ( ) ) ;
187
+ let full_range = self . full_range ( db. upcast ( ) ) ;
205
188
NavigationTargets :: single ( NavigationTarget {
206
- file : class_range . file ( ) ,
207
- focus_range : class_range . range ( ) ,
208
- full_range : self . full_range ( db . upcast ( ) ) . range ( ) ,
189
+ file : full_range . file ( ) ,
190
+ focus_range : self . focus_range ( db . upcast ( ) ) . unwrap_or ( full_range ) . range ( ) ,
191
+ full_range : full_range. range ( ) ,
209
192
} )
210
193
}
211
194
}
212
195
213
- impl HasNavigationTargets for ClassLiteralType < ' _ > {
214
- fn navigation_targets ( & self , db : & dyn Db ) -> NavigationTargets {
215
- self . class ( ) . navigation_targets ( db)
216
- }
217
- }
218
-
219
- impl HasNavigationTargets for InstanceType < ' _ > {
220
- fn navigation_targets ( & self , db : & dyn Db ) -> NavigationTargets {
221
- self . class ( ) . navigation_targets ( db)
222
- }
223
- }
224
-
225
- impl HasNavigationTargets for ModuleLiteralType < ' _ > {
226
- fn navigation_targets ( & self , db : & dyn Db ) -> NavigationTargets {
227
- let file = self . module ( db) . file ( ) ;
228
- let source = source_text ( db. upcast ( ) , file) ;
229
-
230
- NavigationTargets :: single ( NavigationTarget {
231
- file,
232
- focus_range : TextRange :: default ( ) ,
233
- full_range : TextRange :: up_to ( source. text_len ( ) ) ,
234
- } )
235
- }
236
- }
237
-
238
- impl HasNavigationTargets for KnownInstanceType < ' _ > {
239
- fn navigation_targets ( & self , db : & dyn Db ) -> NavigationTargets {
240
- match self {
241
- KnownInstanceType :: TypeVar ( var) => {
242
- let definition = var. definition ( db) ;
243
- let full_range = definition. full_range ( db. upcast ( ) ) ;
244
-
245
- NavigationTargets :: single ( NavigationTarget {
246
- file : full_range. file ( ) ,
247
- focus_range : definition. focus_range ( db. upcast ( ) ) . range ( ) ,
248
- full_range : full_range. range ( ) ,
249
- } )
250
- }
251
-
252
- // TODO: Track the definition of `KnownInstance` and navigate to their definition.
253
- _ => NavigationTargets :: empty ( ) ,
254
- }
255
- }
256
- }
257
-
258
- impl HasNavigationTargets for IntersectionType < ' _ > {
259
- fn navigation_targets ( & self , db : & dyn Db ) -> NavigationTargets {
260
- // Only consider the positive elements because the negative elements are mainly from narrowing constraints.
261
- let mut targets = self
262
- . iter_positive ( db. upcast ( ) )
263
- . filter ( |ty| !ty. is_unknown ( ) ) ;
264
-
265
- let Some ( first) = targets. next ( ) else {
266
- return NavigationTargets :: empty ( ) ;
267
- } ;
268
-
269
- match targets. next ( ) {
270
- Some ( _) => {
271
- // If there are multiple types in the intersection, we can't navigate to a single one
272
- // because the type is the intersection of all those types.
273
- NavigationTargets :: empty ( )
274
- }
275
- None => first. navigation_targets ( db) ,
276
- }
277
- }
278
- }
279
-
280
196
#[ cfg( test) ]
281
197
mod tests {
282
198
use crate :: db:: tests:: TestDb ;
0 commit comments