Skip to content

Conversation

@da-r-k
Copy link

@da-r-k da-r-k commented Feb 9, 2026

Summary

Implements label_for_symbol to provide syntax-highlighted labels in the project symbol picker (cmd-t). Currently, Java symbols appear as plain grey text with no highlighting, unlike Rust and other languages that already implement this.

Ref: zed-industries/zed#36383, zed-industries/zed#37176

Changes

  • Add label_for_symbol to the Extension for Java impl
  • Import Symbol and SymbolKind from the extension API
  • Generate Tree-sitter-parseable code snippets for each symbol kind so the Java grammar produces correct AST nodes and syntax highlighting
  • Wrap member-level constructs (methods, fields, constructors) inside class _ { } since Java's Tree-sitter grammar requires class context for these declarations
  • Display symbols in Java declaration order (type before name): void doWork(String, int), String fieldName
  • Show keyword prefixes for type declarations: class, interface, enum, package

Supported symbol kinds

Kind Display example Tree-sitter context
Class class MyClass class MyClass {}
Interface interface Runnable interface Runnable {}
Enum enum Status enum Status {}
Constructor MyClass(String, int) class MyClass { MyClass() {} }
Method void doWork(String, int) class _ { void doWork() {} }
Field String name class _ { String name; }
Constant MAX_SIZE class _ { static final int MAX_SIZE; }
EnumMember ACTIVE enum _ { ACTIVE }
Variable count class _ { int count; }
Package package com.example package com.example;

Screenshot:

image

Implement `label_for_symbol` to provide syntax-highlighted labels in the
project symbol picker (cmd-t). Without this, Java symbols appear as
plain grey text.

Each symbol kind generates a Tree-sitter-parseable code snippet wrapped
in the appropriate context (e.g. methods/fields inside `class _ { }`)
so the Java grammar can produce correct AST nodes for highlighting.

Supported symbols:
- Class, Interface, Enum: keyword prefix + name
- Method/Function: return type + name + params (Java declaration order)
- Constructor: name + params
- Field/Property: type + name (Java declaration order)
- Constant, EnumMember, Variable: highlighted name
- Package/Module/Namespace: keyword prefix + name

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@cla-bot
Copy link

cla-bot bot commented Feb 9, 2026

We require contributors to sign our Contributor License Agreement, and we don't have @da-r-k on file. You can sign our CLA at https://zed.dev/cla. Once you've signed, post a comment here that says '@cla-bot check'.

@da-r-k
Copy link
Author

da-r-k commented Feb 9, 2026

@cla-bot check

@cla-bot
Copy link

cla-bot bot commented Feb 9, 2026

We require contributors to sign our Contributor License Agreement, and we don't have @da-r-k on file. You can sign our CLA at https://zed.dev/cla. Once you've signed, post a comment here that says '@cla-bot check'.

@cla-bot
Copy link

cla-bot bot commented Feb 9, 2026

The cla-bot has been summoned, and re-checked this pull request!

@da-r-k
Copy link
Author

da-r-k commented Feb 9, 2026

@cla-bot check

@cla-bot cla-bot bot added the cla-signed label Feb 9, 2026
@cla-bot
Copy link

cla-bot bot commented Feb 9, 2026

The cla-bot has been summoned, and re-checked this pull request!

@tartarughina
Copy link
Collaborator

Thanks for the PR.

Please format the code with

cargo fmt --all

and push another commit

@tartarughina
Copy link
Collaborator

@da-r-k I've tried the extension but it doesn't seem to be working as intended.

For example, when searching for the symbol Media, see snippet below, I was just getting Media in the list without any of the additional information about it

@EqualsAndHashCode
public abstract class Media {

    public static final EmptyMedia EMPTY = new EmptyMedia();

}

Are these changes depending on a change to the grammar?

@da-r-k
Copy link
Author

da-r-k commented Feb 9, 2026

@tartarughina

image

Works for me as expected. The highlighting is based on the LSP response (SymbolKind). Could you check once if you built the correct branch?

@tartarughina
Copy link
Collaborator

@da-r-k@da-r-k, I’ve been using the wrong branch. I assumed gh would have retrieved the correct one, but apparently, it wasn’t the case.
I can now see the changes.

Is fuzzy search something that the LSP needs to support to be used or is it up to us to enable it?

@da-r-k
Copy link
Author

da-r-k commented Feb 9, 2026

@tartarughina Yes. JDTLS uses CamelCase fuzzy search. So if you search for EmpMe it would return EmptyMedia as one of the results. It works like the regex Emp*Me*

@tartarughina
Copy link
Collaborator

@da-r-k interesting, I didn't know that it was using CamelCase fuzzy. Since that might be new to a lot, do you mind adding a small entry in the README explaining how project symbol works?

The main points would be:

  • the LSP offers it
  • fuzzy search is available but it expects CamelCase queries

Comment on lines +547 to +577
SymbolKind::Class => {
// code: "class Name {}" → Tree-sitter: class_declaration
// display: "class Name"
let keyword = "class ";
let code = format!("{keyword}{name} {{}}");

Some(CodeLabel {
spans: vec![CodeLabelSpan::code_range(0..keyword.len() + name.len())],
filter_range: (keyword.len()..keyword.len() + name.len()).into(),
code,
})
}
SymbolKind::Interface => {
let keyword = "interface ";
let code = format!("{keyword}{name} {{}}");

Some(CodeLabel {
spans: vec![CodeLabelSpan::code_range(0..keyword.len() + name.len())],
filter_range: (keyword.len()..keyword.len() + name.len()).into(),
code,
})
}
SymbolKind::Enum => {
let keyword = "enum ";
let code = format!("{keyword}{name} {{}}");

Some(CodeLabel {
spans: vec![CodeLabelSpan::code_range(0..keyword.len() + name.len())],
filter_range: (keyword.len()..keyword.len() + name.len()).into(),
code,
})
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code is pretty much the same if not for the keyword, cannot we simplify it removing repeated code?

})
} else {
// No type info, just show the name
let class_open = "class _ { int ";
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does it default to int?

}
SymbolKind::Constant => {
// Wrap in class; ALL_CAPS names get @constant from highlights.scm regex
let class_open = "class _ { static final int ";
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why using int?

})
}
SymbolKind::Variable => {
let class_open = "class _ { int ";
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment as above regarding int usage

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants