@@ -2,17 +2,14 @@ mod db;
22mod find_node;
33mod goto;
44
5- use std:: ops:: { Deref , DerefMut } ;
6-
75pub use db:: Db ;
86pub use goto:: goto_type_definition;
9- use red_knot_python_semantic :: types :: {
10- Class , ClassBase , ClassLiteralType , FunctionType , InstanceType , IntersectionType ,
11- KnownInstanceType , ModuleLiteralType , Type ,
12- } ;
7+ use rustc_hash :: FxHashSet ;
8+ use std :: ops :: { Deref , DerefMut } ;
9+
10+ use red_knot_python_semantic :: types :: { Type , TypeDefinition } ;
1311use ruff_db:: files:: { File , FileRange } ;
14- use ruff_db:: source:: source_text;
15- use ruff_text_size:: { Ranged , TextLen , TextRange } ;
12+ use ruff_text_size:: { Ranged , TextRange } ;
1613
1714/// Information associated with a text range.
1815#[ derive( Debug , Copy , Clone , Eq , PartialEq , Hash ) ]
5451}
5552
5653/// Target to which the editor can navigate to.
57- #[ derive( Debug , Clone ) ]
54+ #[ derive( Debug , Clone , PartialEq , Eq , Hash ) ]
5855pub struct NavigationTarget {
5956 file : File ,
6057
@@ -95,6 +92,17 @@ impl NavigationTargets {
9592 Self ( smallvec:: SmallVec :: new ( ) )
9693 }
9794
95+ fn unique ( targets : impl IntoIterator < Item = NavigationTarget > ) -> Self {
96+ let unique: FxHashSet < _ > = targets. into_iter ( ) . collect ( ) ;
97+ if unique. is_empty ( ) {
98+ Self :: empty ( )
99+ } else {
100+ let mut targets = unique. into_iter ( ) . collect :: < Vec < _ > > ( ) ;
101+ targets. sort_by_key ( |target| ( target. file , target. focus_range . start ( ) ) ) ;
102+ Self ( targets. into ( ) )
103+ }
104+ }
105+
98106 fn iter ( & self ) -> std:: slice:: Iter < ' _ , NavigationTarget > {
99107 self . 0 . iter ( )
100108 }
@@ -125,7 +133,7 @@ impl<'a> IntoIterator for &'a NavigationTargets {
125133
126134impl FromIterator < NavigationTarget > for NavigationTargets {
127135 fn from_iter < T : IntoIterator < Item = NavigationTarget > > ( iter : T ) -> Self {
128- Self ( iter. into_iter ( ) . collect ( ) )
136+ Self :: unique ( iter)
129137 }
130138}
131139
@@ -136,128 +144,46 @@ pub trait HasNavigationTargets {
136144impl HasNavigationTargets for Type < ' _ > {
137145 fn navigation_targets ( & self , db : & dyn Db ) -> NavigationTargets {
138146 match self {
139- Type :: BoundMethod ( method) => method. function ( db) . navigation_targets ( db) ,
140- Type :: FunctionLiteral ( function) => function. navigation_targets ( db) ,
141- Type :: ModuleLiteral ( module) => module. navigation_targets ( db) ,
142147 Type :: Union ( union) => union
143148 . iter ( db. upcast ( ) )
144149 . flat_map ( |target| target. navigation_targets ( db) )
145150 . collect ( ) ,
146- Type :: ClassLiteral ( class) => class. navigation_targets ( db) ,
147- Type :: Instance ( instance) => instance. navigation_targets ( db) ,
148- Type :: KnownInstance ( instance) => instance. navigation_targets ( db) ,
149- Type :: SubclassOf ( subclass_of_type) => match subclass_of_type. subclass_of ( ) {
150- ClassBase :: Class ( class) => class. navigation_targets ( db) ,
151- ClassBase :: Dynamic ( _) => NavigationTargets :: empty ( ) ,
152- } ,
153-
154- Type :: StringLiteral ( _)
155- | Type :: BooleanLiteral ( _)
156- | Type :: LiteralString
157- | Type :: IntLiteral ( _)
158- | Type :: BytesLiteral ( _)
159- | Type :: SliceLiteral ( _)
160- | Type :: MethodWrapper ( _)
161- | Type :: WrapperDescriptor ( _)
162- | Type :: PropertyInstance ( _)
163- | Type :: Tuple ( _) => self . to_meta_type ( db. upcast ( ) ) . navigation_targets ( db) ,
164-
165- Type :: Intersection ( intersection) => intersection. navigation_targets ( db) ,
166-
167- Type :: Dynamic ( _)
168- | Type :: Never
169- | Type :: Callable ( _)
170- | Type :: AlwaysTruthy
171- | Type :: AlwaysFalsy => NavigationTargets :: empty ( ) ,
172- }
173- }
174- }
175-
176- impl HasNavigationTargets for FunctionType < ' _ > {
177- fn navigation_targets ( & self , db : & dyn Db ) -> NavigationTargets {
178- let function_range = self . focus_range ( db. upcast ( ) ) ;
179- NavigationTargets :: single ( NavigationTarget {
180- file : function_range. file ( ) ,
181- focus_range : function_range. range ( ) ,
182- full_range : self . full_range ( db. upcast ( ) ) . range ( ) ,
183- } )
184- }
185- }
186-
187- impl HasNavigationTargets for Class < ' _ > {
188- fn navigation_targets ( & self , db : & dyn Db ) -> NavigationTargets {
189- let class_range = self . focus_range ( db. upcast ( ) ) ;
190- NavigationTargets :: single ( NavigationTarget {
191- file : class_range. file ( ) ,
192- focus_range : class_range. range ( ) ,
193- full_range : self . full_range ( db. upcast ( ) ) . range ( ) ,
194- } )
195- }
196- }
197-
198- impl HasNavigationTargets for ClassLiteralType < ' _ > {
199- fn navigation_targets ( & self , db : & dyn Db ) -> NavigationTargets {
200- self . class ( ) . navigation_targets ( db)
201- }
202- }
203-
204- impl HasNavigationTargets for InstanceType < ' _ > {
205- fn navigation_targets ( & self , db : & dyn Db ) -> NavigationTargets {
206- self . class ( ) . navigation_targets ( db)
207- }
208- }
209-
210- impl HasNavigationTargets for ModuleLiteralType < ' _ > {
211- fn navigation_targets ( & self , db : & dyn Db ) -> NavigationTargets {
212- let file = self . module ( db) . file ( ) ;
213- let source = source_text ( db. upcast ( ) , file) ;
214151
215- NavigationTargets :: single ( NavigationTarget {
216- file,
217- focus_range : TextRange :: default ( ) ,
218- full_range : TextRange :: up_to ( source. text_len ( ) ) ,
219- } )
220- }
221- }
222-
223- impl HasNavigationTargets for KnownInstanceType < ' _ > {
224- fn navigation_targets ( & self , db : & dyn Db ) -> NavigationTargets {
225- match self {
226- KnownInstanceType :: TypeVar ( var) => {
227- let definition = var. definition ( db) ;
228- let full_range = definition. full_range ( db. upcast ( ) ) ;
229-
230- NavigationTargets :: single ( NavigationTarget {
231- file : full_range. file ( ) ,
232- focus_range : definition. focus_range ( db. upcast ( ) ) . range ( ) ,
233- full_range : full_range. range ( ) ,
234- } )
152+ Type :: Intersection ( intersection) => {
153+ // Only consider the positive elements because the negative elements are mainly from narrowing constraints.
154+ let mut targets = intersection
155+ . iter_positive ( db. upcast ( ) )
156+ . filter ( |ty| !ty. is_unknown ( ) ) ;
157+
158+ let Some ( first) = targets. next ( ) else {
159+ return NavigationTargets :: empty ( ) ;
160+ } ;
161+
162+ match targets. next ( ) {
163+ Some ( _) => {
164+ // If there are multiple types in the intersection, we can't navigate to a single one
165+ // because the type is the intersection of all those types.
166+ NavigationTargets :: empty ( )
167+ }
168+ None => first. navigation_targets ( db) ,
169+ }
235170 }
236171
237- // TODO: Track the definition of `KnownInstance` and navigate to their definition.
238- _ => NavigationTargets :: empty ( ) ,
172+ ty => ty
173+ . definition ( db. upcast ( ) )
174+ . map ( |definition| definition. navigation_targets ( db) )
175+ . unwrap_or_else ( NavigationTargets :: empty) ,
239176 }
240177 }
241178}
242179
243- impl HasNavigationTargets for IntersectionType < ' _ > {
180+ impl HasNavigationTargets for TypeDefinition < ' _ > {
244181 fn navigation_targets ( & self , db : & dyn Db ) -> NavigationTargets {
245- // Only consider the positive elements because the negative elements are mainly from narrowing constraints.
246- let mut targets = self
247- . iter_positive ( db. upcast ( ) )
248- . filter ( |ty| !ty. is_unknown ( ) ) ;
249-
250- let Some ( first) = targets. next ( ) else {
251- return NavigationTargets :: empty ( ) ;
252- } ;
253-
254- match targets. next ( ) {
255- Some ( _) => {
256- // If there are multiple types in the intersection, we can't navigate to a single one
257- // because the type is the intersection of all those types.
258- NavigationTargets :: empty ( )
259- }
260- None => first. navigation_targets ( db) ,
261- }
182+ let full_range = self . full_range ( db. upcast ( ) ) ;
183+ NavigationTargets :: single ( NavigationTarget {
184+ file : full_range. file ( ) ,
185+ focus_range : self . focus_range ( db. upcast ( ) ) . unwrap_or ( full_range) . range ( ) ,
186+ full_range : full_range. range ( ) ,
187+ } )
262188 }
263189}
0 commit comments