From bc023b3f80c9daa36365948710813d75b3226eb7 Mon Sep 17 00:00:00 2001 From: Xin Zhao Date: Wed, 25 Feb 2026 21:37:54 +0800 Subject: [PATCH] languages: Improve completion sorting for Python-based LSPs (#47160) Closes #47086 This PR detects completion items ending with `=` (which typically represent keyword arguments in function calls provided by `Pyright`/`BasedPyright`/`pylsp`) and assigns them the highest sorting priority. This ensures that when a user is filling out function arguments, the named parameters appear at the top of the list, rather than being buried mixed with other symbols. After fix: image > **Note on Sorting:** Currently, these named arguments will be sorted alphabetically by label. Preserving the original order of the function definition would be ideal, but it requires information not currently available in this logical block. Insights on how to retrieve the definition order would be appreciated. > **Note on other LSPs:** > * **`ty`**: Already provides well-sorted completions natively, so no intervention is required. Release Notes: - Improved completion order for Python-based LSPs --- crates/languages/src/python.rs | 47 ++++++++++++++++++++++++---------- 1 file changed, 34 insertions(+), 13 deletions(-) diff --git a/crates/languages/src/python.rs b/crates/languages/src/python.rs index b1192464e9601183ac5d91196bfbe529feaa693f..9eaf9764f100428b4bbbc80238f7da5847001470 100644 --- a/crates/languages/src/python.rs +++ b/crates/languages/src/python.rs @@ -112,6 +112,8 @@ impl FromStr for TestRunner { /// Decided to ignore Pyright's sortText() completely and to manually sort all entries fn process_pyright_completions(items: &mut [lsp::CompletionItem]) { for item in items { + let is_named_argument = item.label.ends_with('='); + let is_dunder = item.label.starts_with("__") && item.label.ends_with("__"); let visibility_priority = if is_dunder { @@ -124,23 +126,35 @@ fn process_pyright_completions(items: &mut [lsp::CompletionItem]) { '0' // public }; + let is_external = item + .detail + .as_ref() + .is_some_and(|detail| detail == "Auto-import"); + + let source_priority = if is_external { '1' } else { '0' }; + // Kind priority within same visibility level let kind_priority = match item.kind { - Some(lsp::CompletionItemKind::ENUM_MEMBER) => '0', - Some(lsp::CompletionItemKind::FIELD) => '1', - Some(lsp::CompletionItemKind::PROPERTY) => '2', - Some(lsp::CompletionItemKind::VARIABLE) => '3', - Some(lsp::CompletionItemKind::CONSTANT) => '4', - Some(lsp::CompletionItemKind::METHOD) => '5', - Some(lsp::CompletionItemKind::FUNCTION) => '5', - Some(lsp::CompletionItemKind::CLASS) => '6', - Some(lsp::CompletionItemKind::MODULE) => '7', - _ => '8', + Some(lsp::CompletionItemKind::KEYWORD) => '0', + Some(lsp::CompletionItemKind::ENUM_MEMBER) => '1', + Some(lsp::CompletionItemKind::FIELD) => '2', + Some(lsp::CompletionItemKind::PROPERTY) => '3', + Some(lsp::CompletionItemKind::VARIABLE) => '4', + Some(lsp::CompletionItemKind::CONSTANT) => '5', + Some(lsp::CompletionItemKind::METHOD) => '6', + Some(lsp::CompletionItemKind::FUNCTION) => '6', + Some(lsp::CompletionItemKind::CLASS) => '7', + Some(lsp::CompletionItemKind::MODULE) => '8', + + _ => 'z', }; + // Named arguments get higher priority + let argument_priority = if is_named_argument { '0' } else { '1' }; + item.sort_text = Some(format!( - "{}{}{}", - visibility_priority, kind_priority, item.label + "{}{}{}{}{}", + argument_priority, source_priority, visibility_priority, kind_priority, item.label )); } } @@ -1689,7 +1703,14 @@ impl LspAdapter for PyLspAdapter { Self::SERVER_NAME } - async fn process_completions(&self, _items: &mut [lsp::CompletionItem]) {} + async fn process_completions(&self, items: &mut [lsp::CompletionItem]) { + for item in items { + let is_named_argument = item.label.ends_with('='); + let priority = if is_named_argument { '0' } else { '1' }; + let sort_text = item.sort_text.take().unwrap_or_else(|| item.label.clone()); + item.sort_text = Some(format!("{}{}", priority, sort_text)); + } + } async fn label_for_completion( &self,