@@ -12,7 +12,12 @@ use itertools::Itertools;
1212use lsp_types:: CompletionItem ;
1313use lsp_types:: CompletionItemKind ;
1414use lsp_types:: DocumentSymbol ;
15+ use lsp_types:: ParameterInformation ;
16+ use lsp_types:: ParameterLabel ;
17+ use lsp_types:: SignatureHelp ;
18+ use lsp_types:: SignatureInformation ;
1519use pyrefly_util:: gas:: Gas ;
20+ use pyrefly_util:: prelude:: SliceExt ;
1621use pyrefly_util:: prelude:: VecExt ;
1722use pyrefly_util:: task_heap:: Cancelled ;
1823use pyrefly_util:: visit:: Visit ;
@@ -55,8 +60,11 @@ use crate::state::require::Require;
5560use crate :: state:: state:: CancellableTransaction ;
5661use crate :: state:: state:: Transaction ;
5762use crate :: sys_info:: SysInfo ;
63+ use crate :: types:: callable:: Param ;
64+ use crate :: types:: callable:: Params ;
5865use crate :: types:: lsp:: source_range_to_range;
5966use crate :: types:: module:: Module ;
67+ use crate :: types:: types:: BoundMethodType ;
6068use crate :: types:: types:: Type ;
6169
6270const INITIAL_GAS : Gas = Gas :: new ( 20 ) ;
@@ -321,6 +329,120 @@ impl<'a> Transaction<'a> {
321329 }
322330 }
323331
332+ pub fn get_signature_help_at (
333+ & self ,
334+ handle : & Handle ,
335+ position : TextSize ,
336+ ) -> Option < SignatureHelp > {
337+ let mod_module = self . get_ast ( handle) ?;
338+ fn visit ( x : & Expr , find : TextSize , res : & mut Option < ( TextRange , TextRange , usize ) > ) {
339+ if let Expr :: Call ( call) = x
340+ && call. arguments . range . contains_inclusive ( find)
341+ {
342+ for ( i, arg) in call. arguments . args . as_ref ( ) . iter ( ) . enumerate ( ) {
343+ if arg. range ( ) . contains_inclusive ( find) {
344+ visit ( arg, find, res) ;
345+ if res. is_some ( ) {
346+ return ;
347+ }
348+ * res = Some ( ( call. func . range ( ) , call. arguments . range , i) ) ;
349+ }
350+ }
351+ if res. is_none ( ) {
352+ * res = Some ( (
353+ call. func . range ( ) ,
354+ call. arguments . range ,
355+ call. arguments . len ( ) ,
356+ ) ) ;
357+ }
358+ } else {
359+ x. recurse ( & mut |x| visit ( x, find, res) ) ;
360+ }
361+ }
362+ let mut res = None ;
363+ mod_module. visit ( & mut |x| visit ( x, position, & mut res) ) ;
364+ let ( callee_range, call_args_range, arg_index) = res?;
365+ let answers = self . get_answers ( handle) ?;
366+ if let Some ( ( overloads, chosen_overload_index) ) =
367+ answers. get_all_overload_trace ( call_args_range)
368+ {
369+ let signatures = overloads. into_map ( |callable| {
370+ Self :: create_signature_information ( Type :: Callable ( Box :: new ( callable) ) , arg_index)
371+ } ) ;
372+ Some ( SignatureHelp {
373+ signatures,
374+ active_signature : chosen_overload_index. map ( |i| i as u32 ) ,
375+ active_parameter : Some ( arg_index as u32 ) ,
376+ } )
377+ } else {
378+ answers
379+ . get_type_trace ( callee_range)
380+ . map ( |callee_type| SignatureHelp {
381+ signatures : vec ! [ Self :: create_signature_information(
382+ callee_type. arc_clone( ) ,
383+ arg_index,
384+ ) ] ,
385+ active_signature : Some ( 0 ) ,
386+ active_parameter : Some ( arg_index as u32 ) ,
387+ } )
388+ }
389+ }
390+
391+ fn create_signature_information ( type_ : Type , arg_index : usize ) -> SignatureInformation {
392+ let type_ = type_. deterministic_printing ( ) ;
393+ let label = format ! ( "{}" , type_) ;
394+ let ( parameters, active_parameter) = if let Some ( params) =
395+ Self :: normalize_singleton_function_type_for_signature_help ( type_)
396+ {
397+ let active_parameter = if arg_index < params. len ( ) {
398+ Some ( arg_index as u32 )
399+ } else {
400+ None
401+ } ;
402+ (
403+ Some ( params. map ( |param| ParameterInformation {
404+ label : ParameterLabel :: Simple ( format ! ( "{}" , param) ) ,
405+ documentation : None ,
406+ } ) ) ,
407+ active_parameter,
408+ )
409+ } else {
410+ ( None , None )
411+ } ;
412+ SignatureInformation {
413+ label,
414+ documentation : None ,
415+ parameters,
416+ active_parameter,
417+ }
418+ }
419+
420+ fn normalize_singleton_function_type_for_signature_help ( type_ : Type ) -> Option < Vec < Param > > {
421+ let callable = match type_ {
422+ Type :: Callable ( callable) => Some ( * callable) ,
423+ Type :: Function ( function) => Some ( function. signature ) ,
424+ Type :: BoundMethod ( bound_method) => match bound_method. func {
425+ BoundMethodType :: Function ( function) => Some ( function. signature ) ,
426+ BoundMethodType :: Forall ( forall) => Some ( forall. body . signature ) ,
427+ BoundMethodType :: Overload ( _) => None ,
428+ } ,
429+ _ => None ,
430+ } ?;
431+ // We will drop the self parameter for signature help
432+ if let Params :: List ( params_list) = callable. params {
433+ if let Some ( Param :: PosOnly ( Some ( name) , _, _) | Param :: Pos ( name, _, _) ) =
434+ params_list. items ( ) . first ( )
435+ && name. as_str ( ) == "self"
436+ {
437+ let mut params = params_list. into_items ( ) ;
438+ params. remove ( 0 ) ;
439+ return Some ( params) ;
440+ }
441+ return Some ( params_list. into_items ( ) ) ;
442+ }
443+ None
444+ }
445+
324446 fn resolve_named_import (
325447 & self ,
326448 handle : & Handle ,
0 commit comments