Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,14 @@ protected void toggleCapability(boolean enabled, String id, String capability, O
}

protected <R> CompletableFuture<R> computeAsync(Function<IProgressMonitor, R> code) {
return CompletableFutures.computeAsync(cc -> code.apply(toMonitor(cc)));
return CompletableFutures.computeAsync(cc -> code.apply(toMonitor(cc)))
.whenComplete((result, error) -> {
if (error != null) {
JavaLanguageServerPlugin.logException(
"Unhandled exception in LSP request handler",
error);
}
});
}

protected IProgressMonitor toMonitor(CancelChecker checker) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,12 @@
import org.eclipse.jdt.core.search.IJavaSearchScope;
import org.eclipse.jdt.core.search.SearchEngine;
import org.eclipse.jdt.core.search.SearchMatch;
import org.eclipse.jdt.core.search.SearchParticipant;
import org.eclipse.jdt.core.search.SearchPattern;
import org.eclipse.jdt.core.search.SearchRequestor;
import org.eclipse.jdt.internal.core.BinaryMember;
import org.eclipse.jdt.internal.core.JrtPackageFragmentRoot;
import org.eclipse.jdt.internal.core.manipulation.JavaElementLabelsCore;
import org.eclipse.jdt.internal.core.search.indexing.DerivedSourceSearchParticipantRegistry;
import org.eclipse.jdt.launching.IVMInstall;
import org.eclipse.jdt.launching.IVMInstall2;
import org.eclipse.jdt.launching.JavaRuntime;
Expand Down Expand Up @@ -81,7 +81,36 @@ public class HoverInfoProvider {
private static final long COMMON_SIGNATURE_FLAGS = LABEL_FLAGS & ~JavaElementLabelsCore.ALL_FULLY_QUALIFIED
| JavaElementLabelsCore.T_FULLY_QUALIFIED | JavaElementLabelsCore.M_FULLY_QUALIFIED;

private static final String LANGUAGE_ID = "java";
private static final String DEFAULT_LANGUAGE_ID = "java";

/**
* Returns the LSP language identifier for the given element.
* Queries the DerivedSourceSearchParticipantRegistry for contributed (non-Java)
* elements; falls back to "java" for standard Java elements.
*/
private static String getLanguageId(IJavaElement element) {
if (element != null) {
ICompilationUnit cu = (element instanceof IMember m)
? m.getCompilationUnit()
: (element instanceof ILocalVariable lv)
? (ICompilationUnit) lv.getAncestor(
IJavaElement.COMPILATION_UNIT)
: null;
if (cu != null) {
String fileName = cu.getElementName();
String ext = DerivedSourceSearchParticipantRegistry
.getFileExtension(fileName);
if (ext != null) {
String langId = DerivedSourceSearchParticipantRegistry
.getLanguageId(ext);
if (langId != null) {
return langId;
}
}
}
}
return DEFAULT_LANGUAGE_ID;
}

private final ITypeRoot unit;

Expand Down Expand Up @@ -193,7 +222,7 @@ private boolean isResolved(IJavaElement element, IProgressMonitor monitor) throw
SearchEngine engine = new SearchEngine();
IJavaSearchScope scope = SearchEngine.createJavaSearchScope(new IJavaElement[] { unit }, IJavaSearchScope.SOURCES | IJavaSearchScope.APPLICATION_LIBRARIES | IJavaSearchScope.SYSTEM_LIBRARIES);
try {
engine.search(pattern, new SearchParticipant[] { SearchEngine.getDefaultSearchParticipant() }, scope, new SearchRequestor() {
engine.search(pattern, SearchEngine.getSearchParticipants(), scope, new SearchRequestor() {

@Override
public void acceptSearchMatch(SearchMatch match) throws CoreException {
Expand Down Expand Up @@ -246,7 +275,7 @@ public static MarkedString computeSignature(IJavaElement element) {
elementLabel = elementLabel + " = " + constantValue;
}
}
return new MarkedString(LANGUAGE_ID, elementLabel);
return new MarkedString(getLanguageId(element), elementLabel);
}

private static String getDefaultValue(IMethod method) {
Expand Down Expand Up @@ -292,7 +321,7 @@ public static MarkedString computeJavadoc(IJavaElement element) throws CoreExcep
}
}
}
return result != null ? new MarkedString(LANGUAGE_ID, result) : null;
return result != null ? new MarkedString(getLanguageId(element), result) : null;
}

public static String getSourceInfo(IJavaElement element) throws JavaModelException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,14 @@
import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
import org.eclipse.jdt.core.manipulation.CoreASTProvider;
import org.eclipse.jdt.core.manipulation.SharedASTProviderCore;
import org.eclipse.jdt.core.search.IJavaSearchConstants;
import org.eclipse.jdt.core.search.IJavaSearchScope;
import org.eclipse.jdt.core.search.SearchEngine;
import org.eclipse.jdt.core.search.SearchMatch;
import org.eclipse.jdt.core.search.DerivedSourceSearchParticipant;
import org.eclipse.jdt.core.search.SearchParticipant;
import org.eclipse.jdt.core.search.SearchPattern;
import org.eclipse.jdt.core.search.SearchRequestor;
import org.eclipse.jdt.internal.codeassist.InternalCompletionProposal;
import org.eclipse.jdt.internal.codeassist.impl.Engine;
import org.eclipse.jdt.internal.compiler.lookup.Binding;
Expand Down Expand Up @@ -222,6 +228,15 @@ public static ICompilationUnit resolveCompilationUnit(IFile resource) {
return JavaCore.createCompilationUnitFrom(resource);
}
}
// Fallback: ask contributed search participants for non-Java source files
for (SearchParticipant p : SearchUtils.getContributedSearchParticipants()) {
if (p instanceof DerivedSourceSearchParticipant dsp) {
ICompilationUnit cu = dsp.getCompilationUnit(resource);
if (cu != null) {
return cu;
}
}
}
}

return null;
Expand Down Expand Up @@ -1104,11 +1119,95 @@ public static IJavaElement[] findElementsAtSelection(ITypeRoot unit, int line, i
}
}
}
// Fallback: ask contributed search participants for non-Java types.
// Java's codeSelect returns empty for types provided by non-Java
// languages (e.g., Kotlin facade classes, property accessors).
if ((elements == null || elements.length == 0) && unit.getJavaProject() != null) {
IJavaElement resolved = resolveViaSearchParticipants(unit, offset, monitor);
if (resolved != null) {
return new IJavaElement[] { resolved };
}
}
return elements;
}
return null;
}

/**
* Extracts the Java identifier at the given offset from the buffer and
* searches contributed search participants for a matching TYPE or METHOD
* declaration. Returns the first match, or {@code null} if none found.
* <p>
* This provides a fallback for types and methods provided by non-Java
* languages (e.g., Kotlin facade classes and property accessors) whose
* source is not visible to Java's {@code codeSelect()}.
*/
private static IJavaElement resolveViaSearchParticipants(ITypeRoot unit, int offset, IProgressMonitor monitor) {
try {
IBuffer buffer = unit.getBuffer();
if (buffer == null) {
return null;
}
int length = buffer.getLength();
int start = offset;
while (start > 0 && Character.isJavaIdentifierPart(buffer.getChar(start - 1))) {
start--;
}
int end = offset;
while (end < length && Character.isJavaIdentifierPart(buffer.getChar(end))) {
end++;
}
if (start == end) {
return null;
}
String identifier = buffer.getText(start, end - start);

// Search contributed participants for TYPE declarations first,
// then METHOD declarations (for property accessors like getXxx)
int[] searchTypes = { IJavaSearchConstants.TYPE, IJavaSearchConstants.METHOD };
IJavaSearchScope scope = SearchEngine.createJavaSearchScope(
new IJavaElement[] { unit.getJavaProject() });
SearchEngine engine = new SearchEngine();
SearchParticipant[] participants = SearchEngine.getSearchParticipants();
for (int searchType : searchTypes) {
SearchPattern pattern = SearchPattern.createPattern(
identifier, searchType, IJavaSearchConstants.DECLARATIONS,
SearchPattern.R_EXACT_MATCH | SearchPattern.R_CASE_SENSITIVE);
if (pattern == null) {
continue;
}
IJavaElement[] result = new IJavaElement[1];
try {
engine.search(pattern, participants, scope,
new SearchRequestor() {
@Override
public void acceptSearchMatch(SearchMatch match)
throws CoreException {
if (result[0] == null
&& match.getAccuracy() != SearchMatch.A_INACCURATE
&& match.getElement() instanceof IJavaElement el) {
result[0] = el;
throw new OperationCanceledException();
}
}
}, monitor);
} catch (OperationCanceledException e) {
// first match found — stop searching
} catch (CoreException e) {
JavaLanguageServerPlugin.logException(
"Error resolving via search participants", e);
}
if (result[0] != null) {
return result[0];
}
}
} catch (JavaModelException e) {
JavaLanguageServerPlugin.logException(
"Error extracting identifier for participant search", e);
}
return null;
}

public static boolean isSameParameters(IMethod method1, IMethod method2) {
if (method1 == null || method2 == null) {
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
import java.io.InputStreamReader;
import java.nio.file.Files;

import java.util.Arrays;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.IPath;
Expand All @@ -30,6 +32,8 @@
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.search.SearchEngine;
import org.eclipse.jdt.core.search.SearchParticipant;
import org.eclipse.jdt.ls.core.internal.preferences.PreferenceManager;
import org.eclipse.lsp4j.Location;
import org.eclipse.lsp4j.Position;
Expand Down Expand Up @@ -133,6 +137,20 @@ public void visitLineNumber(int line, Label start) {
}
}

private static final SearchParticipant[] EMPTY_PARTICIPANTS = new SearchParticipant[0];

/**
* Returns contributed search participants, excluding the default Java
* participant. {@link SearchEngine#getSearchParticipants()} places the
* default at index 0; this method returns the remainder.
*/
public static SearchParticipant[] getContributedSearchParticipants() {
SearchParticipant[] all = SearchEngine.getSearchParticipants();
return all.length > 1
? Arrays.copyOfRange(all, 1, all.length)
: EMPTY_PARTICIPANTS;
}

public static Location searchOtherSources(IMember member) throws JavaModelException {
PreferenceManager preferenceManager = JavaLanguageServerPlugin.getPreferencesManager();
if (member == null || member.getClassFile() == null || preferenceManager == null || !(preferenceManager.getPreferences().isAspectjSupportEnabled() || preferenceManager.getPreferences().isKotlinSupportEnabled()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@
import org.eclipse.jdt.core.search.IJavaSearchScope;
import org.eclipse.jdt.core.search.SearchEngine;
import org.eclipse.jdt.core.search.SearchMatch;
import org.eclipse.jdt.core.search.SearchParticipant;
import org.eclipse.jdt.core.search.SearchPattern;
import org.eclipse.jdt.core.search.SearchRequestor;
import org.eclipse.jdt.ls.core.internal.JDTUtils;
Expand Down Expand Up @@ -148,7 +147,7 @@ private List<Location> findReferences(IJavaElement element, IProgressMonitor mon
SearchPattern pattern = SearchPattern.createPattern(element, IJavaSearchConstants.REFERENCES);
final List<Location> result = new ArrayList<>();
SearchEngine engine = new SearchEngine();
engine.search(pattern, new SearchParticipant[] { SearchEngine.getDefaultSearchParticipant() }, createSearchScope(), new SearchRequestor() {
engine.search(pattern, SearchEngine.getSearchParticipants(), createSearchScope(), new SearchRequestor() {

@Override
public void acceptSearchMatch(SearchMatch match) throws CoreException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

import org.eclipse.core.runtime.Assert;
Expand Down Expand Up @@ -56,6 +58,7 @@
import org.eclipse.jdt.internal.corext.util.MethodOverrideTester;
import org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin;
import org.eclipse.jdt.ls.core.internal.Messages;
import org.eclipse.jdt.ls.core.internal.SearchUtils;
import org.eclipse.jface.text.IRegion;


Expand Down Expand Up @@ -136,12 +139,72 @@ private List<T> findTypeImplementations(IProgressMonitor monitor) throws JavaMod
if (monitor.isCanceled()) {
throw new OperationCanceledException();
}
supplementWithContributedImplementations(type, results, allTypes, monitor);
} finally {
monitor.done();
}
return results;
}

/**
* Supplements the implementation list with types found via contributed
* search participants. JDT's type hierarchy only discovers Java subtypes;
* non-Java types (e.g., Kotlin) that implement or extend the target type
* are only discoverable via an IMPLEMENTORS search through contributed
* participants.
*/
private void supplementWithContributedImplementations(IType type,
List<T> results, IType[] alreadyFound,
IProgressMonitor monitor) {
SearchParticipant[] contributed =
SearchUtils.getContributedSearchParticipants();
if (contributed.length == 0) {
return;
}
try {
SearchPattern pattern = SearchPattern.createPattern(
type.getFullyQualifiedName(),
IJavaSearchConstants.TYPE,
IJavaSearchConstants.IMPLEMENTORS,
SearchPattern.R_EXACT_MATCH
| SearchPattern.R_CASE_SENSITIVE);
if (pattern == null) {
return;
}
Set<String> seen = new HashSet<>();
for (IType t : alreadyFound) {
seen.add(t.getFullyQualifiedName());
}
List<IType> foundTypes = new ArrayList<>();
new SearchEngine().search(pattern, contributed,
SearchEngine.createWorkspaceScope(),
new SearchRequestor() {
@Override
public void acceptSearchMatch(SearchMatch match) {
if (match.getElement() instanceof IType t) {
foundTypes.add(t);
}
}
}, monitor);
for (IType foundType : foundTypes) {
if (monitor.isCanceled()) {
return;
}
if (seen.contains(foundType.getFullyQualifiedName())) {
continue;
}
T result = mapper.convert(foundType, 0, 0);
if (result != null) {
results.add(result);
seen.add(foundType.getFullyQualifiedName());
}
}
} catch (CoreException e) {
JavaLanguageServerPlugin.logException(
"Error searching contributed participants for implementations", e);
}
}

private List<T> findMethodImplementations(IProgressMonitor monitor) throws CoreException {
IMethod method = (IMethod) javaElement;
try {
Expand Down Expand Up @@ -217,7 +280,7 @@ public void acceptSearchMatch(SearchMatch match) throws CoreException {
int limitTo = IJavaSearchConstants.DECLARATIONS | IJavaSearchConstants.IGNORE_DECLARING_TYPE | IJavaSearchConstants.IGNORE_RETURN_TYPE;
SearchPattern pattern = SearchPattern.createPattern(method, limitTo);
Assert.isNotNull(pattern);
SearchParticipant[] participants = new SearchParticipant[] { SearchEngine.getDefaultSearchParticipant() };
SearchParticipant[] participants = SearchEngine.getSearchParticipants();
SearchEngine engine = new SearchEngine();
engine.search(pattern, participants, hierarchyScope, requestor, new SubProgressMonitor(monitor, 7));
if (monitor.isCanceled()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1273,6 +1273,12 @@ private <R> CompletableFuture<R> computeAsyncWithClientProgress(Function<IProgre
return CompletableFutures.computeAsync((cc) -> {
IProgressMonitor monitor = progressReporterManager.getProgressReporter(cc);
return code.apply(monitor);
}).whenComplete((result, error) -> {
if (error != null) {
JavaLanguageServerPlugin.logException(
"Unhandled exception in LSP request handler",
error);
}
});
}

Expand Down
Loading