From 65b8b24e992bc55f18f57f570a3e01c292589c99 Mon Sep 17 00:00:00 2001 From: Asuka Minato Date: Fri, 14 Nov 2025 04:57:35 +0900 Subject: [PATCH 1/9] fix --- pyrefly/lib/commands/infer.rs | 1 + pyrefly/lib/state/ide.rs | 28 ++++++++++++++++++++++++++++ pyrefly/lib/state/lsp.rs | 19 ++++++++++++++----- pyrefly/lib/test/lsp/code_actions.rs | 6 ++---- 4 files changed, 45 insertions(+), 9 deletions(-) diff --git a/pyrefly/lib/commands/infer.rs b/pyrefly/lib/commands/infer.rs index b8ab72560c..130950e167 100644 --- a/pyrefly/lib/commands/infer.rs +++ b/pyrefly/lib/commands/infer.rs @@ -25,6 +25,7 @@ use crate::commands::files::get_project_config_for_current_dir; use crate::commands::util::CommandExitStatus; use crate::config::error_kind::ErrorKind; use crate::state::ide::insert_import_edit_with_forced_import_format; +use crate::state::ide::ImportEdit; use crate::state::lsp::AnnotationKind; use crate::state::lsp::ParameterAnnotation; use crate::state::require::Require; diff --git a/pyrefly/lib/state/ide.rs b/pyrefly/lib/state/ide.rs index e3670bda77..b66cd08726 100644 --- a/pyrefly/lib/state/ide.rs +++ b/pyrefly/lib/state/ide.rs @@ -16,6 +16,8 @@ use ruff_python_ast::Expr; use ruff_python_ast::ModModule; use ruff_python_ast::helpers::is_docstring_stmt; use ruff_python_ast::name::Name; +use ruff_python_ast::Stmt; +use ruff_python_ast::StmtImportFrom; use ruff_text_size::Ranged; use ruff_text_size::TextRange; use ruff_text_size::TextSize; @@ -32,6 +34,13 @@ use crate::state::lsp::ImportFormat; const KEY_TO_DEFINITION_INITIAL_GAS: Gas = Gas::new(100); +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct ImportEdit { + pub position: TextSize, + pub insert_text: String, + pub display_text: String, +} + pub enum IntermediateDefinition { Local(Export), NamedImport(TextRange, ModuleName, Name, Option), @@ -242,6 +251,25 @@ pub fn insert_import_edit_with_forced_import_format( } else { handle_to_import_from.module() }; + let display_text = format!( + "from {} import {}", + module_name_to_import.as_str(), + export_name + ); + if let Some((position, insert_text)) = + try_extend_existing_from_import(ast, module_name_to_import.as_str(), export_name) + { + return ImportEdit { + position, + insert_text, + display_text, + }; + } + let position = if let Some(first_stmt) = ast.body.iter().find(|stmt| !is_docstring_stmt(stmt)) { + first_stmt.range().start() + } else { + ast.range.end() + }; let insert_text = format!( "from {} import {}\n", module_name_to_import.as_str(), diff --git a/pyrefly/lib/state/lsp.rs b/pyrefly/lib/state/lsp.rs index 4ec6036bc0..a77e62ffe5 100644 --- a/pyrefly/lib/state/lsp.rs +++ b/pyrefly/lib/state/lsp.rs @@ -1886,9 +1886,15 @@ impl<'a> Transaction<'a> { unknown_name, import_format, ); - let range = TextRange::at(position, TextSize::new(0)); - let title = format!("Insert import: `{}`", insert_text.trim()); - code_actions.push((title, module_info.dupe(), range, insert_text)); + let range = TextRange::at(import_edit.position, TextSize::new(0)); + let title = + format!("Insert import: `{}`", import_edit.display_text); + code_actions.push(( + title, + module_info.dupe(), + range, + import_edit.insert_text, + )); } for module_name in self.search_modules_fuzzy(unknown_name) { @@ -2416,8 +2422,11 @@ impl<'a> Transaction<'a> { import_format, ); let import_text_edit = TextEdit { - range: module_info.to_lsp_range(TextRange::at(position, TextSize::new(0))), - new_text: insert_text.clone(), + range: module_info.to_lsp_range(TextRange::at( + import_edit.position, + TextSize::new(0), + )), + new_text: import_edit.insert_text.clone(), }; (insert_text, Some(vec![import_text_edit]), module_name) }; diff --git a/pyrefly/lib/test/lsp/code_actions.rs b/pyrefly/lib/test/lsp/code_actions.rs index 203406f465..1d4a36afbb 100644 --- a/pyrefly/lib/test/lsp/code_actions.rs +++ b/pyrefly/lib/test/lsp/code_actions.rs @@ -210,8 +210,7 @@ fn insertion_test_duplicate_imports() { ], get_test_report, ); - // The insertion won't attempt to merge imports from the same module. - // It's not illegal, but it would be nice if we do merge. + // When another import from the same module already exists, we should append to it. assert_eq!( r#" # a.py @@ -227,8 +226,7 @@ from a import another_thing my_export # ^ ## After: -from a import my_export -from a import another_thing +from a import another_thing, my_export my_export # ^ "# From 689c942f57e0f39531d13f59f388eaafbb50ffd7 Mon Sep 17 00:00:00 2001 From: Asuka Minato Date: Fri, 14 Nov 2025 04:58:52 +0900 Subject: [PATCH 2/9] fmt --- pyrefly/lib/commands/infer.rs | 2 +- pyrefly/lib/state/ide.rs | 4 ++-- pyrefly/lib/state/lsp.rs | 9 +++------ 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/pyrefly/lib/commands/infer.rs b/pyrefly/lib/commands/infer.rs index 130950e167..317a82e6e4 100644 --- a/pyrefly/lib/commands/infer.rs +++ b/pyrefly/lib/commands/infer.rs @@ -24,8 +24,8 @@ use crate::commands::files::FilesArgs; use crate::commands::files::get_project_config_for_current_dir; use crate::commands::util::CommandExitStatus; use crate::config::error_kind::ErrorKind; -use crate::state::ide::insert_import_edit_with_forced_import_format; use crate::state::ide::ImportEdit; +use crate::state::ide::insert_import_edit_with_forced_import_format; use crate::state::lsp::AnnotationKind; use crate::state::lsp::ParameterAnnotation; use crate::state::require::Require; diff --git a/pyrefly/lib/state/ide.rs b/pyrefly/lib/state/ide.rs index b66cd08726..a9b16b648d 100644 --- a/pyrefly/lib/state/ide.rs +++ b/pyrefly/lib/state/ide.rs @@ -14,10 +14,10 @@ use pyrefly_python::symbol_kind::SymbolKind; use pyrefly_util::gas::Gas; use ruff_python_ast::Expr; use ruff_python_ast::ModModule; -use ruff_python_ast::helpers::is_docstring_stmt; -use ruff_python_ast::name::Name; use ruff_python_ast::Stmt; use ruff_python_ast::StmtImportFrom; +use ruff_python_ast::helpers::is_docstring_stmt; +use ruff_python_ast::name::Name; use ruff_text_size::Ranged; use ruff_text_size::TextRange; use ruff_text_size::TextSize; diff --git a/pyrefly/lib/state/lsp.rs b/pyrefly/lib/state/lsp.rs index a77e62ffe5..eec3c8a8d3 100644 --- a/pyrefly/lib/state/lsp.rs +++ b/pyrefly/lib/state/lsp.rs @@ -1887,8 +1887,7 @@ impl<'a> Transaction<'a> { import_format, ); let range = TextRange::at(import_edit.position, TextSize::new(0)); - let title = - format!("Insert import: `{}`", import_edit.display_text); + let title = format!("Insert import: `{}`", import_edit.display_text); code_actions.push(( title, module_info.dupe(), @@ -2422,10 +2421,8 @@ impl<'a> Transaction<'a> { import_format, ); let import_text_edit = TextEdit { - range: module_info.to_lsp_range(TextRange::at( - import_edit.position, - TextSize::new(0), - )), + range: module_info + .to_lsp_range(TextRange::at(import_edit.position, TextSize::new(0))), new_text: import_edit.insert_text.clone(), }; (insert_text, Some(vec![import_text_edit]), module_name) From e3180ab38d10a1b7855a2d124a1692ad1548dac6 Mon Sep 17 00:00:00 2001 From: Asuka Minato Date: Tue, 18 Nov 2025 19:43:39 +0900 Subject: [PATCH 3/9] fix --- pyrefly/lib/commands/infer.rs | 18 ++++---- pyrefly/lib/state/ide.rs | 78 ++++++++++++++++++++++++++++------- pyrefly/lib/state/lsp.rs | 21 +++++++--- 3 files changed, 87 insertions(+), 30 deletions(-) diff --git a/pyrefly/lib/commands/infer.rs b/pyrefly/lib/commands/infer.rs index 317a82e6e4..5918a778ac 100644 --- a/pyrefly/lib/commands/infer.rs +++ b/pyrefly/lib/commands/infer.rs @@ -290,7 +290,7 @@ impl InferArgs { if let Some(ast) = transaction.get_ast(&handle) { let error_range = error.range(); let unknown_name = module_info.code_at(error_range); - let imports: Vec<(TextSize, String, String)> = transaction + let imports: Vec = transaction .search_exports_exact(unknown_name) .into_iter() .map(|handle_to_import_from| { @@ -335,16 +335,16 @@ impl InferArgs { fs_anyhow::write(file_path, result) } - fn add_imports_to_file( - file_path: &Path, - imports: Vec<(TextSize, String, String)>, - ) -> anyhow::Result<()> { + fn add_imports_to_file(file_path: &Path, imports: Vec) -> anyhow::Result<()> { let file_content = fs_anyhow::read_to_string(file_path)?; let mut result = file_content; - for (position, import, _) in imports { - let offset = (position).into(); - if !result.contains(&import) { - result.insert_str(offset, &import); + for import_edit in imports { + if import_edit.insert_text.is_empty() { + continue; + } + let offset = (import_edit.position).into(); + if offset <= result.len() && !result.contains(&import_edit.insert_text) { + result.insert_str(offset, &import_edit.insert_text); } } fs_anyhow::write(file_path, result) diff --git a/pyrefly/lib/state/ide.rs b/pyrefly/lib/state/ide.rs index a9b16b648d..e0031ca692 100644 --- a/pyrefly/lib/state/ide.rs +++ b/pyrefly/lib/state/ide.rs @@ -39,6 +39,7 @@ pub struct ImportEdit { pub position: TextSize, pub insert_text: String, pub display_text: String, + pub module_name: String, } pub enum IntermediateDefinition { @@ -198,7 +199,7 @@ pub fn insert_import_edit( handle_to_import_from: Handle, export_name: &str, import_format: ImportFormat, -) -> (TextSize, String, String) { +) -> ImportEdit { let use_absolute_import = match import_format { ImportFormat::Absolute => true, ImportFormat::Relative => { @@ -235,12 +236,7 @@ pub fn insert_import_edit_with_forced_import_format( handle_to_import_from: Handle, export_name: &str, use_absolute_import: bool, -) -> (TextSize, String, String) { - let position = if let Some(first_stmt) = ast.body.iter().find(|stmt| !is_docstring_stmt(stmt)) { - first_stmt.range().start() - } else { - ast.range.end() - }; +) -> ImportEdit { let module_name_to_import = if use_absolute_import { handle_to_import_from.module() } else if let Some(relative_module) = ModuleName::relative_module_name_between( @@ -256,14 +252,14 @@ pub fn insert_import_edit_with_forced_import_format( module_name_to_import.as_str(), export_name ); - if let Some((position, insert_text)) = - try_extend_existing_from_import(ast, module_name_to_import.as_str(), export_name) - { - return ImportEdit { - position, - insert_text, - display_text, - }; + if let Some(edit) = try_extend_existing_from_import( + ast, + module_name_to_import.as_str(), + export_name, + display_text.clone(), + module_name_to_import.as_str(), + ) { + return edit; } let position = if let Some(first_stmt) = ast.body.iter().find(|stmt| !is_docstring_stmt(stmt)) { first_stmt.range().start() @@ -275,7 +271,12 @@ pub fn insert_import_edit_with_forced_import_format( module_name_to_import.as_str(), export_name ); - (position, insert_text, module_name_to_import.to_string()) + ImportEdit { + position, + insert_text, + display_text, + module_name: module_name_to_import.to_string(), + } } /// Some handles must be imported in absolute style, @@ -299,3 +300,48 @@ fn handle_require_absolute_import(config_finder: &ConfigFinder, handle: &Handle) .site_package_path() .any(|search_path| handle.path().as_path().starts_with(search_path)) } + +fn try_extend_existing_from_import( + ast: &ModModule, + target_module_name: &str, + export_name: &str, + display_text: String, + module_name: &str, +) -> Option { + for stmt in &ast.body { + if let Stmt::ImportFrom(import_from) = stmt { + if import_from_module_name(import_from) == target_module_name { + if import_from + .names + .iter() + .any(|alias| alias.asname.is_none() && alias.name.as_str() == export_name) + { + // Already imported; don't propose a duplicate edit. + return None; + } + if let Some(last_alias) = import_from.names.last() { + let position = last_alias.range.end(); + let insert_text = format!(", {}", export_name); + return Some(ImportEdit { + position, + insert_text, + display_text, + module_name: module_name.to_owned(), + }); + } + } + } + } + None +} + +fn import_from_module_name(import_from: &StmtImportFrom) -> String { + let mut module_name = String::new(); + if import_from.level > 0 { + module_name.push_str(&".".repeat(import_from.level as usize)); + } + if let Some(module) = &import_from.module { + module_name.push_str(module.as_str()); + } + module_name +} diff --git a/pyrefly/lib/state/lsp.rs b/pyrefly/lib/state/lsp.rs index eec3c8a8d3..0e9438c800 100644 --- a/pyrefly/lib/state/lsp.rs +++ b/pyrefly/lib/state/lsp.rs @@ -1878,7 +1878,7 @@ impl<'a> Transaction<'a> { if error_range.contains_range(range) { let unknown_name = module_info.code_at(error_range); for handle_to_import_from in self.search_exports_exact(unknown_name) { - let (position, insert_text, _) = insert_import_edit( + let import_edit = insert_import_edit( &ast, self.config_finder(), handle.dupe(), @@ -1886,6 +1886,10 @@ impl<'a> Transaction<'a> { unknown_name, import_format, ); + // If the symbol was already imported we get an empty edit; skip it. + if import_edit.insert_text.is_empty() { + continue; + } let range = TextRange::at(import_edit.position, TextSize::new(0)); let title = format!("Insert import: `{}`", import_edit.display_text); code_actions.push(( @@ -2411,8 +2415,8 @@ impl<'a> Transaction<'a> { continue; } let module_description = handle_to_import_from.module().as_str().to_owned(); - let (insert_text, additional_text_edits, imported_module) = { - let (position, insert_text, module_name) = insert_import_edit( + let (detail_text, additional_text_edits, imported_module) = { + let import_edit = insert_import_edit( &ast, self.config_finder(), handle.dupe(), @@ -2420,12 +2424,19 @@ impl<'a> Transaction<'a> { &name, import_format, ); + if import_edit.insert_text.is_empty() { + continue; + } let import_text_edit = TextEdit { range: module_info .to_lsp_range(TextRange::at(import_edit.position, TextSize::new(0))), new_text: import_edit.insert_text.clone(), }; - (insert_text, Some(vec![import_text_edit]), module_name) + ( + Some(import_edit.display_text), + Some(vec![import_text_edit]), + import_edit.module_name, + ) }; let auto_import_label_detail = format!(" (import {imported_module})"); let (label, label_details) = if supports_completion_item_details { @@ -2441,7 +2452,7 @@ impl<'a> Transaction<'a> { }; completions.push(CompletionItem { label, - detail: Some(insert_text), + detail: detail_text, kind: export .symbol_kind .map_or(Some(CompletionItemKind::VARIABLE), |k| { From d58661998121e2267434e365183f8949ab3e74a2 Mon Sep 17 00:00:00 2001 From: Asuka Minato Date: Tue, 18 Nov 2025 20:46:20 +0900 Subject: [PATCH 4/9] fix --- pyrefly/lib/commands/infer.rs | 1 + pyrefly/lib/state/ide.rs | 20 ++++++++++++-------- pyrefly/lib/state/lsp.rs | 9 ++++++--- pyrefly/lib/test/lsp/completion.rs | 8 +++++++- 4 files changed, 26 insertions(+), 12 deletions(-) diff --git a/pyrefly/lib/commands/infer.rs b/pyrefly/lib/commands/infer.rs index 5918a778ac..d75faaa916 100644 --- a/pyrefly/lib/commands/infer.rs +++ b/pyrefly/lib/commands/infer.rs @@ -300,6 +300,7 @@ impl InferArgs { handle_to_import_from.dupe(), unknown_name, true, + /*merge_with_existing=*/ false, ) }) .collect(); diff --git a/pyrefly/lib/state/ide.rs b/pyrefly/lib/state/ide.rs index e0031ca692..d9e66563c2 100644 --- a/pyrefly/lib/state/ide.rs +++ b/pyrefly/lib/state/ide.rs @@ -212,6 +212,7 @@ pub fn insert_import_edit( handle_to_import_from, export_name, use_absolute_import, + true, ) } @@ -236,6 +237,7 @@ pub fn insert_import_edit_with_forced_import_format( handle_to_import_from: Handle, export_name: &str, use_absolute_import: bool, + merge_with_existing: bool, ) -> ImportEdit { let module_name_to_import = if use_absolute_import { handle_to_import_from.module() @@ -252,14 +254,16 @@ pub fn insert_import_edit_with_forced_import_format( module_name_to_import.as_str(), export_name ); - if let Some(edit) = try_extend_existing_from_import( - ast, - module_name_to_import.as_str(), - export_name, - display_text.clone(), - module_name_to_import.as_str(), - ) { - return edit; + if merge_with_existing { + if let Some(edit) = try_extend_existing_from_import( + ast, + module_name_to_import.as_str(), + export_name, + display_text.clone(), + module_name_to_import.as_str(), + ) { + return edit; + } } let position = if let Some(first_stmt) = ast.body.iter().find(|stmt| !is_docstring_stmt(stmt)) { first_stmt.range().start() diff --git a/pyrefly/lib/state/lsp.rs b/pyrefly/lib/state/lsp.rs index 0e9438c800..003d449f67 100644 --- a/pyrefly/lib/state/lsp.rs +++ b/pyrefly/lib/state/lsp.rs @@ -2389,6 +2389,7 @@ impl<'a> Transaction<'a> { } } + fn add_autoimport_completions( &self, handle: &Handle, @@ -2405,9 +2406,11 @@ impl<'a> Transaction<'a> { && let Some(ast) = self.get_ast(handle) && let Some(module_info) = self.get_module_info(handle) { - for (handle_to_import_from, name, export) in - self.search_exports_fuzzy(identifier.as_str()) - { + let search_results = self.search_exports_fuzzy(identifier.as_str()); + for (handle_to_import_from, name, export) in search_results { + if !identifier.as_str().starts_with('_') && name.starts_with('_') { + continue; + } // Using handle itself doesn't always work because handles can be made separately and have different hashes if handle_to_import_from.module() == handle.module() || handle_to_import_from.module() == ModuleName::builtins() diff --git a/pyrefly/lib/test/lsp/completion.rs b/pyrefly/lib/test/lsp/completion.rs index 06767478be..5c492ebef7 100644 --- a/pyrefly/lib/test/lsp/completion.rs +++ b/pyrefly/lib/test/lsp/completion.rs @@ -87,7 +87,7 @@ fn get_test_report( report.push_str("[DEPRECATED] "); } report.push_str(&label); - if let Some(detail) = detail { + if let Some(detail) = &detail { report.push_str(": "); report.push_str(&detail); } @@ -100,6 +100,7 @@ fn get_test_report( report.push_str(" with text edit: "); report.push_str(&format!("{:?}", &text_edit)); } + let mut added_extra_line = false; if let Some(documentation) = documentation { report.push('\n'); match documentation { @@ -110,6 +111,11 @@ fn get_test_report( report.push_str(&content.value); } } + added_extra_line = true; + } + if detail.is_some() || added_extra_line { + // Insert a blank line between rich (import/doc) entries for readability. + report.push('\n'); } } } From 1b9659e6169b7a230ec3d7913d79f09a8b2269f1 Mon Sep 17 00:00:00 2001 From: Asuka Minato Date: Tue, 18 Nov 2025 20:52:26 +0900 Subject: [PATCH 5/9] fmt --- pyrefly/lib/state/lsp.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/pyrefly/lib/state/lsp.rs b/pyrefly/lib/state/lsp.rs index 003d449f67..e939a73585 100644 --- a/pyrefly/lib/state/lsp.rs +++ b/pyrefly/lib/state/lsp.rs @@ -2389,7 +2389,6 @@ impl<'a> Transaction<'a> { } } - fn add_autoimport_completions( &self, handle: &Handle, From a2a589bb872a59b621aa78c21fb203fcfc11e2d6 Mon Sep 17 00:00:00 2001 From: Asuka Minato Date: Tue, 18 Nov 2025 22:37:14 +0900 Subject: [PATCH 6/9] try --- pyrefly/lib/state/lsp.rs | 11 ++++++----- pyrefly/lib/test/lsp/completion.rs | 8 +------- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/pyrefly/lib/state/lsp.rs b/pyrefly/lib/state/lsp.rs index e939a73585..03f86d9c1f 100644 --- a/pyrefly/lib/state/lsp.rs +++ b/pyrefly/lib/state/lsp.rs @@ -2435,7 +2435,7 @@ impl<'a> Transaction<'a> { new_text: import_edit.insert_text.clone(), }; ( - Some(import_edit.display_text), + Some(import_edit.insert_text.clone()), Some(vec![import_text_edit]), import_edit.module_name, ) @@ -2871,10 +2871,11 @@ impl<'a> Transaction<'a> { self.add_builtins_autoimport_completions(handle, None, &mut result); } self.add_literal_completions(handle, position, &mut result); - // in foo(x=<>, y=2<>), the first containing node is AnyNodeRef::Arguments(_) - // in foo(<>), the first containing node is AnyNodeRef::ExprCall - if let Some(first) = nodes.first() - && matches!(first, AnyNodeRef::ExprCall(_) | AnyNodeRef::Arguments(_)) + // Only offer kwargs when the cursor isn't inside a literal value. + if !nodes.iter().any(|n| matches!(n, AnyNodeRef::ExprStringLiteral(_))) + && nodes + .iter() + .any(|n| matches!(n, AnyNodeRef::ExprCall(_) | AnyNodeRef::Arguments(_))) { self.add_kwargs_completions(handle, position, &mut result); } diff --git a/pyrefly/lib/test/lsp/completion.rs b/pyrefly/lib/test/lsp/completion.rs index 5c492ebef7..6dc3b66cbb 100644 --- a/pyrefly/lib/test/lsp/completion.rs +++ b/pyrefly/lib/test/lsp/completion.rs @@ -100,8 +100,7 @@ fn get_test_report( report.push_str(" with text edit: "); report.push_str(&format!("{:?}", &text_edit)); } - let mut added_extra_line = false; - if let Some(documentation) = documentation { + if let Some(ref documentation) = documentation { report.push('\n'); match documentation { lsp_types::Documentation::String(s) => { @@ -111,11 +110,6 @@ fn get_test_report( report.push_str(&content.value); } } - added_extra_line = true; - } - if detail.is_some() || added_extra_line { - // Insert a blank line between rich (import/doc) entries for readability. - report.push('\n'); } } } From 3e44a75df890c28d0d004195d20292052e60462c Mon Sep 17 00:00:00 2001 From: Asuka Minato Date: Tue, 18 Nov 2025 22:41:18 +0900 Subject: [PATCH 7/9] clippy --- pyrefly/lib/state/ide.rs | 10 ++++------ pyrefly/lib/state/lsp.rs | 10 ++++++---- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/pyrefly/lib/state/ide.rs b/pyrefly/lib/state/ide.rs index d9e66563c2..76e1466ff0 100644 --- a/pyrefly/lib/state/ide.rs +++ b/pyrefly/lib/state/ide.rs @@ -254,8 +254,8 @@ pub fn insert_import_edit_with_forced_import_format( module_name_to_import.as_str(), export_name ); - if merge_with_existing { - if let Some(edit) = try_extend_existing_from_import( + if merge_with_existing + && let Some(edit) = try_extend_existing_from_import( ast, module_name_to_import.as_str(), export_name, @@ -264,7 +264,6 @@ pub fn insert_import_edit_with_forced_import_format( ) { return edit; } - } let position = if let Some(first_stmt) = ast.body.iter().find(|stmt| !is_docstring_stmt(stmt)) { first_stmt.range().start() } else { @@ -313,8 +312,8 @@ fn try_extend_existing_from_import( module_name: &str, ) -> Option { for stmt in &ast.body { - if let Stmt::ImportFrom(import_from) = stmt { - if import_from_module_name(import_from) == target_module_name { + if let Stmt::ImportFrom(import_from) = stmt + && import_from_module_name(import_from) == target_module_name { if import_from .names .iter() @@ -334,7 +333,6 @@ fn try_extend_existing_from_import( }); } } - } } None } diff --git a/pyrefly/lib/state/lsp.rs b/pyrefly/lib/state/lsp.rs index 03f86d9c1f..23ebd9fe5e 100644 --- a/pyrefly/lib/state/lsp.rs +++ b/pyrefly/lib/state/lsp.rs @@ -2872,10 +2872,12 @@ impl<'a> Transaction<'a> { } self.add_literal_completions(handle, position, &mut result); // Only offer kwargs when the cursor isn't inside a literal value. - if !nodes.iter().any(|n| matches!(n, AnyNodeRef::ExprStringLiteral(_))) - && nodes - .iter() - .any(|n| matches!(n, AnyNodeRef::ExprCall(_) | AnyNodeRef::Arguments(_))) + if !nodes + .iter() + .any(|n| matches!(n, AnyNodeRef::ExprStringLiteral(_))) + && nodes.iter().any(|n| { + matches!(n, AnyNodeRef::ExprCall(_) | AnyNodeRef::Arguments(_)) + }) { self.add_kwargs_completions(handle, position, &mut result); } From 605a78014027df1404de904ba67864479608aba8 Mon Sep 17 00:00:00 2001 From: Asuka Minato Date: Tue, 18 Nov 2025 22:51:05 +0900 Subject: [PATCH 8/9] fmt --- pyrefly/lib/state/ide.rs | 46 +++++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/pyrefly/lib/state/ide.rs b/pyrefly/lib/state/ide.rs index 76e1466ff0..b604fb2916 100644 --- a/pyrefly/lib/state/ide.rs +++ b/pyrefly/lib/state/ide.rs @@ -261,9 +261,10 @@ pub fn insert_import_edit_with_forced_import_format( export_name, display_text.clone(), module_name_to_import.as_str(), - ) { - return edit; - } + ) + { + return edit; + } let position = if let Some(first_stmt) = ast.body.iter().find(|stmt| !is_docstring_stmt(stmt)) { first_stmt.range().start() } else { @@ -313,26 +314,27 @@ fn try_extend_existing_from_import( ) -> Option { for stmt in &ast.body { if let Stmt::ImportFrom(import_from) = stmt - && import_from_module_name(import_from) == target_module_name { - if import_from - .names - .iter() - .any(|alias| alias.asname.is_none() && alias.name.as_str() == export_name) - { - // Already imported; don't propose a duplicate edit. - return None; - } - if let Some(last_alias) = import_from.names.last() { - let position = last_alias.range.end(); - let insert_text = format!(", {}", export_name); - return Some(ImportEdit { - position, - insert_text, - display_text, - module_name: module_name.to_owned(), - }); - } + && import_from_module_name(import_from) == target_module_name + { + if import_from + .names + .iter() + .any(|alias| alias.asname.is_none() && alias.name.as_str() == export_name) + { + // Already imported; don't propose a duplicate edit. + return None; } + if let Some(last_alias) = import_from.names.last() { + let position = last_alias.range.end(); + let insert_text = format!(", {}", export_name); + return Some(ImportEdit { + position, + insert_text, + display_text, + module_name: module_name.to_owned(), + }); + } + } } None } From b4676a31fd7b1676c333643e3d5eda7182cdd14d Mon Sep 17 00:00:00 2001 From: Asuka Minato Date: Fri, 21 Nov 2025 20:58:33 +0900 Subject: [PATCH 9/9] fix --- pyrefly/lib/commands/infer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyrefly/lib/commands/infer.rs b/pyrefly/lib/commands/infer.rs index bf232141e0..c558d55f17 100644 --- a/pyrefly/lib/commands/infer.rs +++ b/pyrefly/lib/commands/infer.rs @@ -25,8 +25,8 @@ use crate::commands::files::FilesArgs; use crate::commands::files::get_project_config_for_current_dir; use crate::commands::util::CommandExitStatus; use crate::config::error_kind::ErrorKind; -use crate::state::ide::ImportEdit; use crate::lsp::wasm::inlay_hints::ParameterAnnotation; +use crate::state::ide::ImportEdit; use crate::state::ide::insert_import_edit_with_forced_import_format; use crate::state::lsp::AnnotationKind; use crate::state::require::Require;