Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@ Here is a common `settings.json` including the above mentioned configurations:
}
```

## Project Symbol Search

The extension supports project-wide symbol search with syntax-highlighted results. This feature is powered by JDTLS and can be accessed via Zed's symbol search.

JDTLS uses **CamelCase fuzzy matching** for symbol queries. For example, searching for `EmpMe` would match `EmptyMedia`. The pattern works like `Emp*Me*`, matching the capital letters of CamelCase names.

## Debugger

Debug support is enabled via our [Fork of Java Debug](https://github.com/zed-industries/java-debug), which the extension will automatically download and start for you. Please refer to the [Zed Documentation](https://zed.dev/docs/debugger#getting-started) for general information about how debugging works in Zed.
Expand Down
67 changes: 66 additions & 1 deletion src/java.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use zed_extension_api::{
self as zed, CodeLabel, CodeLabelSpan, DebugAdapterBinary, DebugTaskDefinition, Extension,
LanguageServerId, LanguageServerInstallationStatus, StartDebuggingRequestArguments,
StartDebuggingRequestArgumentsRequest, Worktree,
lsp::{Completion, CompletionKind},
lsp::{Completion, CompletionKind, Symbol, SymbolKind},
register_extension,
serde_json::{Value, json},
set_language_server_installation_status,
Expand Down Expand Up @@ -535,6 +535,71 @@ impl Extension for Java {
_ => None,
})
}

fn label_for_symbol(
&self,
_language_server_id: &LanguageServerId,
symbol: Symbol,
) -> Option<CodeLabel> {
let name = &symbol.name;

match symbol.kind {
SymbolKind::Class | SymbolKind::Interface | SymbolKind::Enum => {
let keyword = match symbol.kind {
SymbolKind::Class => "class ",
SymbolKind::Interface => "interface ",
SymbolKind::Enum => "enum ",
_ => unreachable!(),
};
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::Method | SymbolKind::Function => {
// jdtls: "methodName(Type, Type) : ReturnType" or "methodName(Type)"
// display: "ReturnType methodName(Type, Type)" (Java declaration order)
let method_name = name.split('(').next().unwrap_or(name);
let after_name = &name[method_name.len()..];

let (params, return_type) = if let Some((p, r)) = after_name.split_once(" : ") {
(p, Some(r))
} else {
(after_name, None)
};

let ret = return_type.unwrap_or("void");
let class_open = "class _ { ";
let code = format!("{class_open}{ret} {method_name}() {{}} }}");

let ret_start = class_open.len();
let name_start = ret_start + ret.len() + 1;

// Display: "void methodName(String, int)"
let mut spans = vec![
CodeLabelSpan::code_range(ret_start..ret_start + ret.len()),
CodeLabelSpan::literal(" ".to_string(), None),
CodeLabelSpan::code_range(name_start..name_start + method_name.len()),
];
if !params.is_empty() {
spans.push(CodeLabelSpan::literal(params.to_string(), None));
}

// filter on "methodName(params)" portion of displayed text
let type_prefix_len = ret.len() + 1; // "void "
let filter_end = type_prefix_len + method_name.len() + params.len();
Some(CodeLabel {
spans,
filter_range: (type_prefix_len..filter_end).into(),
code,
})
}
_ => None,
}
}
}

register_extension!(Java);