diff --git a/pyrefly/lib/lsp/wasm/inlay_hints.rs b/pyrefly/lib/lsp/wasm/inlay_hints.rs index a16f63171f..22ac352179 100644 --- a/pyrefly/lib/lsp/wasm/inlay_hints.rs +++ b/pyrefly/lib/lsp/wasm/inlay_hints.rs @@ -429,10 +429,18 @@ impl<'a> Transaction<'a> { .keywords .iter() .any(|kw| kw.value.range() == arg.range()); + let keyword_args_before = call + .arguments + .keywords + .iter() + .filter(|kw| kw.range().start() < arg.range().start()) + .count(); if !is_keyword_arg - && let Some(param_match) = - Self::param_name_for_positional_argument(¶ms, arg_idx) + && let Some(param_match) = Self::param_name_for_positional_argument( + ¶ms, + arg_idx + keyword_args_before, + ) && !param_match.is_vararg_repeat && param_match.name.as_str() != "self" && param_match.name.as_str() != "cls" diff --git a/pyrefly/lib/test/lsp/inlay_hint.rs b/pyrefly/lib/test/lsp/inlay_hint.rs index c46e5efd0a..93c4a1cd70 100644 --- a/pyrefly/lib/test/lsp/inlay_hint.rs +++ b/pyrefly/lib/test/lsp/inlay_hint.rs @@ -417,6 +417,38 @@ obj.method(5, "world") ); } +#[test] +fn test_parameter_name_hints_after_keyword_insertion() { + let code = r#" +def example(a: str, b: str) -> None: + pass + +# This is the transient source after inserting the first positional hint. +example(a="a", "b") +"#; + let files = [("main", code)]; + let (handles, state) = mk_multi_file_state(&files, Require::Exports, false); + let hints = state + .transaction() + .inlay_hints( + handles.get("main").unwrap(), + InlayHintConfig { + call_argument_names: AllOffPartial::All, + variable_types: false, + ..Default::default() + }, + ) + .unwrap(); + let labels: Vec = hints + .into_iter() + .filter_map(|hint| { + let label: String = hint.label_parts.into_iter().map(|(text, _)| text).collect(); + label.ends_with("= ").then_some(label) + }) + .collect(); + assert_eq!(labels, vec!["b= "]); +} + #[test] fn test_parameter_name_hints_with_variable_types() { let code = r#"