@@ -478,6 +478,17 @@ pub struct FindDefinitionItem {
478478 pub module : Module ,
479479}
480480
481+ /// The currently active argument in a function call for signature help.
482+ #[ derive( Debug ) ]
483+ enum ActiveArgument {
484+ /// The cursor is within an existing positional argument at the given index.
485+ Positional ( usize ) ,
486+ /// The cursor is within a keyword argument whose name is provided.
487+ Keyword ( Name ) ,
488+ /// The cursor is in the argument list but not inside any argument expression yet.
489+ Next ( usize ) ,
490+ }
491+
481492impl < ' a > Transaction < ' a > {
482493 fn get_type ( & self , handle : & Handle , key : & Key ) -> Option < Type > {
483494 let idx = self . get_bindings ( handle) ?. key_to_idx ( key) ;
@@ -848,42 +859,93 @@ impl<'a> Transaction<'a> {
848859 fn visit_finding_signature_range (
849860 x : & Expr ,
850861 find : TextSize ,
851- res : & mut Option < ( TextRange , TextRange , usize ) > ,
862+ res : & mut Option < ( TextRange , TextRange , ActiveArgument ) > ,
852863 ) {
853864 if let Expr :: Call ( call) = x
854865 && call. arguments . range . contains_inclusive ( find)
855866 {
856- for ( i, arg) in call. arguments . args . as_ref ( ) . iter ( ) . enumerate ( ) {
857- if arg. range ( ) . contains_inclusive ( find) {
858- Self :: visit_finding_signature_range ( arg, find, res) ;
859- if res. is_some ( ) {
860- return ;
861- }
862- * res = Some ( ( call. func . range ( ) , call. arguments . range , i) ) ;
863- }
867+ if Self :: visit_positional_signature_args ( call, find, res) {
868+ return ;
869+ }
870+ if Self :: visit_keyword_signature_args ( call, find, res) {
871+ return ;
864872 }
865873 if res. is_none ( ) {
866874 * res = Some ( (
867875 call. func . range ( ) ,
868876 call. arguments . range ,
869- call. arguments . len ( ) ,
877+ ActiveArgument :: Next ( call. arguments . len ( ) ) ,
870878 ) ) ;
871879 }
872880 } else {
873881 x. recurse ( & mut |x| Self :: visit_finding_signature_range ( x, find, res) ) ;
874882 }
875883 }
876884
885+ fn visit_positional_signature_args (
886+ call : & ExprCall ,
887+ find : TextSize ,
888+ res : & mut Option < ( TextRange , TextRange , ActiveArgument ) > ,
889+ ) -> bool {
890+ for ( i, arg) in call. arguments . args . as_ref ( ) . iter ( ) . enumerate ( ) {
891+ if arg. range ( ) . contains_inclusive ( find) {
892+ Self :: visit_finding_signature_range ( arg, find, res) ;
893+ if res. is_some ( ) {
894+ return true ;
895+ }
896+ * res = Some ( (
897+ call. func . range ( ) ,
898+ call. arguments . range ,
899+ ActiveArgument :: Positional ( i) ,
900+ ) ) ;
901+ return true ;
902+ }
903+ }
904+ false
905+ }
906+
907+ fn visit_keyword_signature_args (
908+ call : & ExprCall ,
909+ find : TextSize ,
910+ res : & mut Option < ( TextRange , TextRange , ActiveArgument ) > ,
911+ ) -> bool {
912+ let kwarg_start_idx = call. arguments . args . len ( ) ;
913+ for ( j, kw) in call. arguments . keywords . iter ( ) . enumerate ( ) {
914+ if kw. range . contains_inclusive ( find) {
915+ Self :: visit_finding_signature_range ( & kw. value , find, res) ;
916+ if res. is_some ( ) {
917+ return true ;
918+ }
919+ let active_argument = match kw. arg . as_ref ( ) {
920+ Some ( identifier) => ActiveArgument :: Keyword ( identifier. id . clone ( ) ) ,
921+ None => ActiveArgument :: Positional ( kwarg_start_idx + j) ,
922+ } ;
923+ * res = Some ( ( call. func . range ( ) , call. arguments . range , active_argument) ) ;
924+ return true ;
925+ }
926+ }
927+ false
928+ }
929+
877930 /// Finds the callable(s) (multiple if overloads exist) at position in document, returning them, chosen overload index, and arg index
878931 fn get_callables_from_call (
879932 & self ,
880933 handle : & Handle ,
881934 position : TextSize ,
882- ) -> Option < ( Vec < Type > , usize , usize ) > {
935+ ) -> Option < ( Vec < Type > , usize , ActiveArgument ) > {
883936 let mod_module = self . get_ast ( handle) ?;
884937 let mut res = None ;
885938 mod_module. visit ( & mut |x| Self :: visit_finding_signature_range ( x, position, & mut res) ) ;
886- let ( callee_range, call_args_range, arg_index) = res?;
939+ let ( callee_range, call_args_range, mut active_argument) = res?;
940+ // When the cursor is in the argument list but not inside any argument yet,
941+ // estimate the would-be positional index by counting commas up to the cursor.
942+ // This keeps signature help useful even before the user starts typing the next arg.
943+ if let ActiveArgument :: Next ( index) = & mut active_argument
944+ && let Some ( next_index) =
945+ self . count_argument_separators_before ( handle, call_args_range, position)
946+ {
947+ * index = next_index;
948+ }
887949 let answers = self . get_answers ( handle) ?;
888950 if let Some ( ( overloads, chosen_overload_index) ) =
889951 answers. get_all_overload_trace ( call_args_range)
@@ -892,12 +954,12 @@ impl<'a> Transaction<'a> {
892954 Some ( (
893955 callables,
894956 chosen_overload_index. unwrap_or_default ( ) ,
895- arg_index ,
957+ active_argument ,
896958 ) )
897959 } else {
898960 answers
899961 . get_type_trace ( callee_range)
900- . map ( |t| ( vec ! [ t] , 0 , arg_index ) )
962+ . map ( |t| ( vec ! [ t] , 0 , active_argument ) )
901963 }
902964 }
903965
@@ -907,27 +969,33 @@ impl<'a> Transaction<'a> {
907969 position : TextSize ,
908970 ) -> Option < SignatureHelp > {
909971 self . get_callables_from_call ( handle, position) . map (
910- |( callables, chosen_overload_index, arg_index ) | SignatureHelp {
911- signatures : callables
972+ |( callables, chosen_overload_index, active_argument ) | {
973+ let signatures = callables
912974 . into_iter ( )
913- . map ( |t| Self :: create_signature_information ( t, arg_index) )
914- . collect_vec ( ) ,
915- active_signature : Some ( chosen_overload_index as u32 ) ,
916- active_parameter : Some ( arg_index as u32 ) ,
975+ . map ( |t| Self :: create_signature_information ( t, & active_argument) )
976+ . collect_vec ( ) ;
977+ let active_parameter = signatures
978+ . get ( chosen_overload_index)
979+ . and_then ( |info| info. active_parameter ) ;
980+ SignatureHelp {
981+ signatures,
982+ active_signature : Some ( chosen_overload_index as u32 ) ,
983+ active_parameter,
984+ }
917985 } ,
918986 )
919987 }
920988
921- fn create_signature_information ( type_ : Type , arg_index : usize ) -> SignatureInformation {
989+ fn create_signature_information (
990+ type_ : Type ,
991+ active_argument : & ActiveArgument ,
992+ ) -> SignatureInformation {
922993 let type_ = type_. deterministic_printing ( ) ;
923994 let label = type_. as_hover_string ( ) ;
924995 let ( parameters, active_parameter) =
925996 if let Some ( params) = Self :: normalize_singleton_function_type_into_params ( type_) {
926- let active_parameter = if arg_index < params. len ( ) {
927- Some ( arg_index as u32 )
928- } else {
929- None
930- } ;
997+ let active_parameter =
998+ Self :: active_parameter_index ( & params, active_argument) . map ( |idx| idx as u32 ) ;
931999 (
9321000 Some ( params. map ( |param| ParameterInformation {
9331001 label : ParameterLabel :: Simple ( format ! ( "{param}" ) ) ,
@@ -946,6 +1014,35 @@ impl<'a> Transaction<'a> {
9461014 }
9471015 }
9481016
1017+ fn active_parameter_index ( params : & [ Param ] , active_argument : & ActiveArgument ) -> Option < usize > {
1018+ match active_argument {
1019+ ActiveArgument :: Positional ( index) | ActiveArgument :: Next ( index) => {
1020+ ( * index < params. len ( ) ) . then_some ( * index)
1021+ }
1022+ ActiveArgument :: Keyword ( name) => params
1023+ . iter ( )
1024+ . position ( |param| param. name ( ) . is_some_and ( |param_name| param_name == name) ) ,
1025+ }
1026+ }
1027+
1028+ fn count_argument_separators_before (
1029+ & self ,
1030+ handle : & Handle ,
1031+ arguments_range : TextRange ,
1032+ position : TextSize ,
1033+ ) -> Option < usize > {
1034+ let module = self . get_module_info ( handle) ?;
1035+ let contents = module. contents ( ) ;
1036+ let len = contents. len ( ) ;
1037+ let start = arguments_range. start ( ) . to_usize ( ) . min ( len) ;
1038+ let end = arguments_range. end ( ) . to_usize ( ) . min ( len) ;
1039+ let pos = position. to_usize ( ) . clamp ( start, end) ;
1040+ contents
1041+ . get ( start..pos)
1042+ . map ( |slice| slice. bytes ( ) . filter ( |& b| b == b',' ) . count ( ) )
1043+ . or ( Some ( 0 ) )
1044+ }
1045+
9491046 fn normalize_singleton_function_type_into_params ( type_ : Type ) -> Option < Vec < Param > > {
9501047 let callable = type_. to_callable ( ) ?;
9511048 // We will drop the self parameter for signature help
@@ -2269,11 +2366,12 @@ impl<'a> Transaction<'a> {
22692366 position : TextSize ,
22702367 completions : & mut Vec < CompletionItem > ,
22712368 ) {
2272- if let Some ( ( callables, chosen_overload_index, arg_index ) ) =
2369+ if let Some ( ( callables, chosen_overload_index, active_argument ) ) =
22732370 self . get_callables_from_call ( handle, position)
22742371 && let Some ( callable) = callables. get ( chosen_overload_index)
22752372 && let Some ( params) =
22762373 Self :: normalize_singleton_function_type_into_params ( callable. clone ( ) )
2374+ && let Some ( arg_index) = Self :: active_parameter_index ( & params, & active_argument)
22772375 && let Some ( param) = params. get ( arg_index)
22782376 {
22792377 Self :: add_literal_completions_from_type ( param. as_type ( ) , completions) ;
0 commit comments