Skip to content
Closed
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.SearchParticipantRegistry;
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 SearchParticipantRegistry 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 = SearchParticipantRegistry
.getFileExtension(fileName);
if (ext != null) {
String langId = SearchParticipantRegistry
.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,13 @@
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.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 +227,15 @@ public static ICompilationUnit resolveCompilationUnit(IFile resource) {
return JavaCore.createCompilationUnitFrom(resource);
}
}
// Fallback: ask contributed search participants for non-Java source files
// Skip index 0 (default JavaSearchParticipant) — it never provides a CU
SearchParticipant[] participants = SearchEngine.getSearchParticipants();
for (int i = 1; i < participants.length; i++) {
ICompilationUnit cu = participants[i].getCompilationUnit(resource);
if (cu != null) {
return cu;
}
}
}

return null;
Expand Down Expand Up @@ -1104,11 +1118,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 @@ -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 @@ -217,7 +217,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
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ private Location computeDefinitionNavigation(ITypeRoot unit, int line, int colum
if (monitor.isCanceled()) {
return null;
}
if (element == null) {
if (element == null && JavaCore.isJavaLikeFileName(unit.getElementName())) {
return computeBreakContinue(unit, line, column);
}
return computeDefinitionNavigation(element, unit.getJavaProject());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,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.internal.corext.codemanipulation.GetterSetterUtil;
Expand Down Expand Up @@ -177,17 +176,25 @@ private boolean isInsideJRE(IJavaElement element) {
public void search(IJavaElement elementToSearch, final List<Location> locations, IProgressMonitor monitor, boolean isIncludeDeclaration) throws CoreException, JavaModelException {
boolean includeClassFiles = preferenceManager.isClientSupportsClassFileContent();
boolean includeDecompiledSources = preferenceManager.getPreferences().isIncludeDecompiledSources();
// When the search target is a non-Java element (e.g., from
// a contributed SearchParticipant like Kotlin), the Java
// MatchLocator cannot fully resolve the declaring type
// binding and reports matches as A_INACCURATE. These
// matches are still valid — the method name and parameter
// count match — so accept them.
ICompilationUnit cu = (elementToSearch instanceof IMember m) ? m.getCompilationUnit() : null;
boolean acceptInaccurate = cu != null && !JavaCore.isJavaLikeFileName(cu.getElementName());
SearchEngine engine = new SearchEngine();
SearchPattern pattern = SearchPattern.createPattern(elementToSearch, IJavaSearchConstants.REFERENCES);
if (isIncludeDeclaration) {
SearchPattern patternDecl = SearchPattern.createPattern(elementToSearch, IJavaSearchConstants.DECLARATIONS);
pattern = SearchPattern.createOrPattern(pattern, patternDecl);
}
engine.search(pattern, new SearchParticipant[] { SearchEngine.getDefaultSearchParticipant() }, createSearchScope(elementToSearch), new SearchRequestor() {
engine.search(pattern, SearchEngine.getSearchParticipants(), createSearchScope(elementToSearch), new SearchRequestor() {

@Override
public void acceptSearchMatch(SearchMatch match) throws CoreException {
if (match.getAccuracy() == SearchMatch.A_INACCURATE) {
if (!acceptInaccurate && match.getAccuracy() == SearchMatch.A_INACCURATE) {
return;
}
Object o = match.getElement();
Expand Down
Loading