@@ -477,8 +477,11 @@ pub struct FindDefinitionItem {
477477/// The currently active argument in a function call for signature help.
478478#[ derive( Debug ) ]
479479enum ActiveArgument {
480+ /// The cursor is within an existing positional argument at the given index.
480481 Positional ( usize ) ,
482+ /// The cursor is within a keyword argument whose name is provided.
481483 Keyword ( Name ) ,
484+ /// The cursor is in the argument list but not inside any argument expression yet.
482485 Next ( usize ) ,
483486}
484487
@@ -857,36 +860,11 @@ impl<'a> Transaction<'a> {
857860 if let Expr :: Call ( call) = x
858861 && call. arguments . range . contains_inclusive ( find)
859862 {
860- // Check positional arguments
861- for ( i, arg) in call. arguments . args . as_ref ( ) . iter ( ) . enumerate ( ) {
862- if arg. range ( ) . contains_inclusive ( find) {
863- Self :: visit_finding_signature_range ( arg, find, res) ;
864- if res. is_some ( ) {
865- return ;
866- }
867- * res = Some ( (
868- call. func . range ( ) ,
869- call. arguments . range ,
870- ActiveArgument :: Positional ( i) ,
871- ) ) ;
872- return ;
873- }
863+ if Self :: visit_positional_signature_args ( call, find, res) {
864+ return ;
874865 }
875- // Check keyword arguments
876- let kwarg_start_idx = call. arguments . args . len ( ) ;
877- for ( j, kw) in call. arguments . keywords . iter ( ) . enumerate ( ) {
878- if kw. range . contains_inclusive ( find) {
879- Self :: visit_finding_signature_range ( & kw. value , find, res) ;
880- if res. is_some ( ) {
881- return ;
882- }
883- let active_argument = match kw. arg . as_ref ( ) {
884- Some ( identifier) => ActiveArgument :: Keyword ( identifier. id . clone ( ) ) ,
885- None => ActiveArgument :: Positional ( kwarg_start_idx + j) ,
886- } ;
887- * res = Some ( ( call. func . range ( ) , call. arguments . range , active_argument) ) ;
888- return ;
889- }
866+ if Self :: visit_keyword_signature_args ( call, find, res) {
867+ return ;
890868 }
891869 if res. is_none ( ) {
892870 * res = Some ( (
@@ -900,6 +878,51 @@ impl<'a> Transaction<'a> {
900878 }
901879 }
902880
881+ fn visit_positional_signature_args (
882+ call : & ExprCall ,
883+ find : TextSize ,
884+ res : & mut Option < ( TextRange , TextRange , ActiveArgument ) > ,
885+ ) -> bool {
886+ for ( i, arg) in call. arguments . args . as_ref ( ) . iter ( ) . enumerate ( ) {
887+ if arg. range ( ) . contains_inclusive ( find) {
888+ Self :: visit_finding_signature_range ( arg, find, res) ;
889+ if res. is_some ( ) {
890+ return true ;
891+ }
892+ * res = Some ( (
893+ call. func . range ( ) ,
894+ call. arguments . range ,
895+ ActiveArgument :: Positional ( i) ,
896+ ) ) ;
897+ return true ;
898+ }
899+ }
900+ false
901+ }
902+
903+ fn visit_keyword_signature_args (
904+ call : & ExprCall ,
905+ find : TextSize ,
906+ res : & mut Option < ( TextRange , TextRange , ActiveArgument ) > ,
907+ ) -> bool {
908+ let kwarg_start_idx = call. arguments . args . len ( ) ;
909+ for ( j, kw) in call. arguments . keywords . iter ( ) . enumerate ( ) {
910+ if kw. range . contains_inclusive ( find) {
911+ Self :: visit_finding_signature_range ( & kw. value , find, res) ;
912+ if res. is_some ( ) {
913+ return true ;
914+ }
915+ let active_argument = match kw. arg . as_ref ( ) {
916+ Some ( identifier) => ActiveArgument :: Keyword ( identifier. id . clone ( ) ) ,
917+ None => ActiveArgument :: Positional ( kwarg_start_idx + j) ,
918+ } ;
919+ * res = Some ( ( call. func . range ( ) , call. arguments . range , active_argument) ) ;
920+ return true ;
921+ }
922+ }
923+ false
924+ }
925+
903926 /// Finds the callable(s) (multiple if overloads exist) at position in document, returning them, chosen overload index, and arg index
904927 fn get_callables_from_call (
905928 & self ,
@@ -910,6 +933,9 @@ impl<'a> Transaction<'a> {
910933 let mut res = None ;
911934 mod_module. visit ( & mut |x| Self :: visit_finding_signature_range ( x, position, & mut res) ) ;
912935 let ( callee_range, call_args_range, mut active_argument) = res?;
936+ // When the cursor is in the argument list but not inside any argument yet,
937+ // estimate the would-be positional index by counting commas up to the cursor.
938+ // This keeps signature help useful even before the user starts typing the next arg.
913939 if let ActiveArgument :: Next ( index) = & mut active_argument
914940 && let Some ( next_index) =
915941 self . count_argument_separators_before ( handle, call_args_range, position)
@@ -989,20 +1015,9 @@ impl<'a> Transaction<'a> {
9891015 ActiveArgument :: Positional ( index) | ActiveArgument :: Next ( index) => {
9901016 ( * index < params. len ( ) ) . then_some ( * index)
9911017 }
992- ActiveArgument :: Keyword ( name) => params. iter ( ) . position ( |param| {
993- Self :: parameter_name ( param) . is_some_and ( |param_name| param_name == name)
994- } ) ,
995- }
996- }
997-
998- fn parameter_name ( param : & Param ) -> Option < & Name > {
999- match param {
1000- Param :: PosOnly ( Some ( name) , ..)
1001- | Param :: Pos ( name, ..)
1002- | Param :: VarArg ( Some ( name) , ..)
1003- | Param :: KwOnly ( name, ..)
1004- | Param :: Kwargs ( Some ( name) , ..) => Some ( name) ,
1005- _ => None ,
1018+ ActiveArgument :: Keyword ( name) => params
1019+ . iter ( )
1020+ . position ( |param| param. name ( ) . is_some_and ( |param_name| param_name == name) ) ,
10061021 }
10071022 }
10081023
@@ -1014,14 +1029,14 @@ impl<'a> Transaction<'a> {
10141029 ) -> Option < usize > {
10151030 let module = self . get_module_info ( handle) ?;
10161031 let contents = module. contents ( ) ;
1017- let start = arguments_range. start ( ) . to_usize ( ) ;
1018- let end = arguments_range. end ( ) . to_usize ( ) . min ( contents. len ( ) ) ;
1019- if start >= end {
1020- return Some ( 0 ) ;
1021- }
1032+ let len = contents. len ( ) ;
1033+ let start = arguments_range. start ( ) . to_usize ( ) . min ( len) ;
1034+ let end = arguments_range. end ( ) . to_usize ( ) . min ( len) ;
10221035 let pos = position. to_usize ( ) . clamp ( start, end) ;
1023- let slice = & contents[ start..pos] ;
1024- Some ( slice. bytes ( ) . filter ( |& b| b == b',' ) . count ( ) )
1036+ contents
1037+ . get ( start..pos)
1038+ . map ( |slice| slice. bytes ( ) . filter ( |& b| b == b',' ) . count ( ) )
1039+ . or ( Some ( 0 ) )
10251040 }
10261041
10271042 fn normalize_singleton_function_type_into_params ( type_ : Type ) -> Option < Vec < Param > > {
0 commit comments