From f5c19126fbc2a5b61b273a8a99bb0c769968d77b Mon Sep 17 00:00:00 2001 From: Xin Zhao Date: Wed, 24 Jun 2026 16:04:40 +0800 Subject: [PATCH] languages: Fix completion item highlighting for Python (#59758) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Objective Consider a simple Python file with the following content, where both a method and a function are defined: ```python class TestCase: def a_test_method(self): # a method print("This is a method test") def a_test_function(): # a function print("This is a function test") ``` Below this, if we type `a_test_func` and trigger language server completion, we would expect to see `a_test_function` in the completion list. Similarly, if we have: ```python test = TestCase() test.a_testˇ ``` and trigger language server completion, we should see `a_test_method` in the completion list. In both cases, we expect the items in the completion list to have the same color highlighting as they do in the editor buffer, but currently, they do not. This is because Zed uses the wrong queries to retrieve the highlight. Taking the `ty` LSP adapter as an example: https://github.com/zed-industries/zed/blob/d753a31db53e40c47cfb59964102f07c5493eb4f/crates/languages/src/python.rs#L323-L332 the key `"function.method"` and `"function"` are used to look up the method and function highlight IDs, but these keys do not exist in `crates/grammars/src/python/highlights.scm`, which defines the syntax highlighting. The correct keys here should be `"function.method.call"` and `"function.call"`. ## Solution - As stated above, change the keys from `"function.method"` and `"function"` to `"function.method.call"` and `"function.call"`, respectively. - Additionally, since we have several built-in language servers for Python, a minor refactoring has been done to reduce duplicate code and simplify future changes. ## Testing - I have tested this change locally; the before and after comparison is provided below. ## Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content adheres to Zed's UI standards ([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) and [icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md) guidelines) - [ ] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable ## Showcase
Click to view showcase | Before | After | | :---: | :---: | | function_before | function_after | | method_before | method_after |
--- Release Notes: - Improved Python completion item highlighting for methods and functions. --- crates/languages/src/python.rs | 51 +++++++++++++++------------------- 1 file changed, 23 insertions(+), 28 deletions(-) diff --git a/crates/languages/src/python.rs b/crates/languages/src/python.rs index 2734c205ac8..7159379aed6 100644 --- a/crates/languages/src/python.rs +++ b/crates/languages/src/python.rs @@ -14,7 +14,7 @@ use language::{ use language::{ContextProvider, LspAdapter, LspAdapterDelegate}; use language::{LanguageName, ManifestName, ManifestProvider, ManifestQuery}; use language::{Toolchain, ToolchainList, ToolchainLister, ToolchainMetadata}; -use lsp::{LanguageServerBinary, Uri}; +use lsp::{CompletionItemKind, LanguageServerBinary, Uri}; use lsp::{LanguageServerBinaryOptions, LanguageServerName}; use node_runtime::{NodeRuntime, VersionStrategy}; use pet_core::Configuration; @@ -193,16 +193,8 @@ fn label_for_pyright_completion( let label = &item.label; let label_len = label.len(); let grammar = language.grammar()?; - let highlight_id = match item.kind? { - lsp::CompletionItemKind::METHOD => grammar.highlight_id_for_name("function.method"), - lsp::CompletionItemKind::FUNCTION => grammar.highlight_id_for_name("function"), - lsp::CompletionItemKind::CLASS => grammar.highlight_id_for_name("type"), - lsp::CompletionItemKind::CONSTANT => grammar.highlight_id_for_name("constant"), - lsp::CompletionItemKind::VARIABLE => grammar.highlight_id_for_name("variable"), - _ => { - return None; - } - }; + let highlight_id = highlight_id_for_completion(item.kind?, grammar)?; + let mut text = label.clone(); if let Some(completion_details) = item .label_details @@ -255,6 +247,24 @@ fn label_for_python_symbol( )) } +/// Returns the highlight ID for the given completion item kind, if it is supported. +/// +/// The outer `Option` is `None` if the item kind returned by the language server is not covered. +/// The inner `Option` is `None` if the item kind is covered, but the highlight name is not present in the grammar. +fn highlight_id_for_completion( + item_kind: CompletionItemKind, + grammar: &Arc, +) -> Option> { + match item_kind { + CompletionItemKind::METHOD => Some(grammar.highlight_id_for_name("function.method.call")), + CompletionItemKind::FUNCTION => Some(grammar.highlight_id_for_name("function.call")), + CompletionItemKind::CLASS => Some(grammar.highlight_id_for_name("type")), + CompletionItemKind::CONSTANT => Some(grammar.highlight_id_for_name("constant")), + CompletionItemKind::VARIABLE => Some(grammar.highlight_id_for_name("variable")), + _ => None, + } +} + pub struct TyLspAdapter { fs: Arc, } @@ -320,16 +330,7 @@ impl LspAdapter for TyLspAdapter { let label = &item.label; let label_len = label.len(); let grammar = language.grammar()?; - let highlight_id = match item.kind? { - lsp::CompletionItemKind::METHOD => grammar.highlight_id_for_name("function.method"), - lsp::CompletionItemKind::FUNCTION => grammar.highlight_id_for_name("function"), - lsp::CompletionItemKind::CLASS => grammar.highlight_id_for_name("type"), - lsp::CompletionItemKind::CONSTANT => grammar.highlight_id_for_name("constant"), - lsp::CompletionItemKind::VARIABLE => grammar.highlight_id_for_name("variable"), - _ => { - return None; - } - }; + let highlight_id = highlight_id_for_completion(item.kind?, grammar)?; let mut text = label.clone(); if let Some(completion_details) = item @@ -1814,13 +1815,7 @@ impl LspAdapter for PyLspAdapter { let label = &item.label; let label_len = label.len(); let grammar = language.grammar()?; - let highlight_id = match item.kind? { - lsp::CompletionItemKind::METHOD => grammar.highlight_id_for_name("function.method")?, - lsp::CompletionItemKind::FUNCTION => grammar.highlight_id_for_name("function")?, - lsp::CompletionItemKind::CLASS => grammar.highlight_id_for_name("type")?, - lsp::CompletionItemKind::CONSTANT => grammar.highlight_id_for_name("constant")?, - _ => return None, - }; + let highlight_id = highlight_id_for_completion(item.kind?, grammar)??; Some(language::CodeLabel::filtered( label.clone(), label_len,