python: Fix autocomplete sorting (#44050)

Alexander Andreev created

Closes:
#38727 (Python autocompletion being sorted alphabetically)
<img
src="https://github.com/user-attachments/assets/8208d511-f4a4-41f9-8550-3b24370bf776"
width="400">

<img
src="https://github.com/user-attachments/assets/e7c99d7a-b61e-463b-b0f1-36cd279b3887"
width="400">


Release Notes:
- Improve sort order of pyright/basedpyright code completions

Change summary

crates/languages/src/python.rs | 34 +++++++++++++++++++++++++++++++++-
1 file changed, 33 insertions(+), 1 deletion(-)

Detailed changes

crates/languages/src/python.rs 🔗

@@ -101,9 +101,41 @@ impl FromStr for TestRunner {
 /// The problem with it is that Pyright adjusts the sort text based on previous resolutions (items for which we've issued `completion/resolve` call have their sortText adjusted),
 /// which - long story short - makes completion items list non-stable. Pyright probably relies on VSCode's implementation detail.
 /// see https://github.com/microsoft/pyright/blob/95ef4e103b9b2f129c9320427e51b73ea7cf78bd/packages/pyright-internal/src/languageService/completionProvider.ts#LL2873
+///
+/// upd 02.12.25:
+/// 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 {
-        item.sort_text.take();
+        let is_dunder = item.label.starts_with("__") && item.label.ends_with("__");
+
+        let visibility_priority = if is_dunder {
+            '3'
+        } else if item.label.starts_with("__") {
+            '2' // private non-dunder
+        } else if item.label.starts_with('_') {
+            '1' // protected
+        } else {
+            '0' // public
+        };
+
+        // 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',
+        };
+
+        item.sort_text = Some(format!(
+            "{}{}{}",
+            visibility_priority, kind_priority, item.label
+        ));
     }
 }