diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/HoverInfoProvider.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/HoverInfoProvider.java index e8a5247e86..c98a0437d1 100644 --- a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/HoverInfoProvider.java +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/HoverInfoProvider.java @@ -15,7 +15,6 @@ import java.util.LinkedList; import java.util.List; import java.util.Optional; -import java.util.stream.Stream; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; @@ -101,33 +100,11 @@ public List> computeHover(int line, int column, IPr if (monitor.isCanceled()) { return cancelled(res); } - IJavaElement[] elements = JDTUtils.findElementsAtSelection(unit, line, column, this.preferenceManager, monitor); - if (elements == null || elements.length == 0 || monitor.isCanceled()) { - return cancelled(res); - } - IJavaElement curr = null; - if (elements.length != 1) { - // they could be package fragments. - // We need to select the one that matches the package fragment of the current unit - IPackageFragment packageFragment = (IPackageFragment) unit.getParent(); - IJavaElement found = - Stream - .of(elements) - .filter(e -> e.equals(packageFragment)) - .findFirst() - .orElse(null); - if (found == null) { - // this would be a binary package fragment - curr = elements[0]; - } else { - curr = found; - } - } else { - curr = elements[0]; - } - if (monitor.isCanceled()) { + IJavaElement curr = JDTUtils.findElementAtSelection(unit, line, column, this.preferenceManager, monitor); + if (curr == null || monitor.isCanceled()) { return cancelled(res); } + if (JDTEnvironmentUtils.isSyntaxServer() || isResolved(curr, monitor)) { IBuffer buffer = curr.getOpenable().getBuffer(); if (buffer == null && curr instanceof BinaryMember binaryMember) { diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/JDTUtils.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/JDTUtils.java index d5c384fdb5..7ac861399e 100644 --- a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/JDTUtils.java +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/JDTUtils.java @@ -1078,10 +1078,32 @@ public static IJavaElement findElementAtSelection(ITypeRoot unit, int line, int if (monitor.isCanceled()) { return null; } - if (elements != null && elements.length == 1) { + if (elements == null || elements.length == 0) { + return null; + } + if (elements.length == 1) { return elements[0]; } - return null; + return resolveElementAtSelection(unit, line, column, elements); + } + + /** + * When multiple elements are at the same position, returns the one whose name matches + * the identifier at the cursor. Works for any element type (packages, modules, types, etc.). + */ + private static IJavaElement resolveElementAtSelection(ITypeRoot unit, int line, int column, IJavaElement[] elements) throws JavaModelException { + IBuffer buffer = unit.getBuffer(); + if (buffer != null) { + String identifier = getIdentifierAt(buffer, line, column); + if (identifier != null) { + for (IJavaElement e : elements) { + if (identifier.equals(e.getElementName())) { + return e; + } + } + } + } + return elements[0]; } public static IJavaElement[] findElementsAtSelection(ITypeRoot unit, int line, int column, PreferenceManager preferenceManager, IProgressMonitor monitor) throws JavaModelException { @@ -1109,6 +1131,41 @@ public static IJavaElement[] findElementsAtSelection(ITypeRoot unit, int line, i return null; } + /** + * Returns the identifier at the given position in the buffer, including dots (e.g. for module names like "java.xml"). + * Used when multiple elements are at the same position (e.g. JEP 511 module imports) to pick the one matching the source. + * + * @param buffer the buffer, or null + * @param line 0-based line + * @param column 0-based column + * @return the identifier at position, or null if none or buffer is null + */ + public static String getIdentifierAt(IBuffer buffer, int line, int column) { + if (buffer == null) { + return null; + } + int offset = JsonRpcHelpers.toOffset(buffer, line, column); + if (offset < 0 || offset >= buffer.getLength()) { + return null; + } + int start = offset; + while (start > 0 && isIdentifierPart(buffer.getChar(start - 1))) { + start--; + } + int end = offset; + while (end < buffer.getLength() && isIdentifierPart(buffer.getChar(end))) { + end++; + } + if (start >= end) { + return null; + } + return buffer.getText(start, end - start); + } + + private static boolean isIdentifierPart(char c) { + return Character.isLetterOrDigit(c) || c == '_' || c == '.'; + } + public static boolean isSameParameters(IMethod method1, IMethod method2) { if (method1 == null || method2 == null) { return false;