Skip to content

Commit

Permalink
Only return jdt:// urls when supported by the client
Browse files Browse the repository at this point in the history
Clients must declare they support classFileContents (hence jdt:// urls)
by initializing the server with `"classFileContentsSupport": true`

```
    "initializationOptions": {
        "bundles": [...],
        "workspaceFolders": [...],
        "settings": {...},
        "extendedClientCapabilities": {
            "classFileContentsSupport": true
        }
    }
```

If classFileContents is not supported, then no workspace symbols, references, or navigation
will be available for any type in a binary file.

Signed-off-by: Fred Bricon <fbricon@gmail.com>
  • Loading branch information
fbricon committed May 15, 2018
1 parent 0ed1bf2 commit fdfde47
Show file tree
Hide file tree
Showing 16 changed files with 176 additions and 49 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,10 @@ public static Location toLocation(IClassFile classFile, int offset, int length)
}

public static String toUri(IClassFile classFile) {
if (JavaLanguageServerPlugin.getPreferencesManager() != null && !JavaLanguageServerPlugin.getPreferencesManager().isClientSupportsClassFileContent()) {
return null;
}

String packageName = classFile.getParent().getElementName();
String jarName = classFile.getParent().getParent().getElementName();
String uriString = null;
Expand Down Expand Up @@ -549,7 +553,7 @@ public static IJavaElement[] findElementsAtSelection(ITypeRoot unit, int line, i
SearchPattern pattern = SearchPattern.createPattern(name, IJavaSearchConstants.TYPE,
IJavaSearchConstants.DECLARATIONS, SearchPattern.R_FULL_MATCH);

IJavaSearchScope scope = createSearchScope(unit.getJavaProject());
IJavaSearchScope scope = createSearchScope(unit.getJavaProject(), preferenceManager);

List<IJavaElement> elements = new ArrayList<>();
SearchRequestor requestor = new SearchRequestor() {
Expand Down Expand Up @@ -711,12 +715,18 @@ private static boolean isSilencedGeneratedAnnotation(IAnnotation annotation) thr
return false;
}

public static IJavaSearchScope createSearchScope(IJavaProject project) {
if (project == null) {
return SearchEngine.createWorkspaceScope();
public static IJavaSearchScope createSearchScope(IJavaProject project, PreferenceManager preferenceManager) {
IJavaProject[] elements;
if (project == null) {//workspace search
elements = ProjectUtils.getJavaProjects();
} else {
elements = new IJavaProject[] { project };
}
int scope = IJavaSearchScope.SOURCES;
if (preferenceManager != null && preferenceManager.isClientSupportsClassFileContent()) {
scope |= IJavaSearchScope.APPLICATION_LIBRARIES | IJavaSearchScope.SYSTEM_LIBRARIES;
}
return SearchEngine.createJavaSearchScope(new IJavaProject[] { project },
IJavaSearchScope.SOURCES | IJavaSearchScope.APPLICATION_LIBRARIES | IJavaSearchScope.SYSTEM_LIBRARIES);
return SearchEngine.createJavaSearchScope(elements, scope);
}

public static boolean isOnClassPath(ICompilationUnit unit) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -421,4 +421,11 @@ public JavaClientConnection getClientConnection() {
}
return null;
}

//Public for testing purposes
public static void setPreferencesManager(PreferenceManager preferenceManager) {
if (pluginInstance != null) {
pluginInstance.preferenceManager = preferenceManager;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,20 @@ public static String getJavaSourceLevel(IProject project) {
}

public static List<IProject> getGradleProjects() {
IProject[] projects = ResourcesPlugin.getWorkspace().getRoot().getProjects();
return Stream.of(projects).filter(ProjectUtils::isGradleProject).collect(Collectors.toList());
return Stream.of(getAllProjects()).filter(ProjectUtils::isGradleProject).collect(Collectors.toList());
}

public static IJavaProject[] getJavaProjects() {
//@formatter:off
return Stream.of(getAllProjects())
.filter(ProjectUtils::isJavaProject)
.map(p -> JavaCore.create(p))
.filter(p -> p != null)
.toArray(IJavaProject[]::new);
//@formatter:on
}

public static IProject[] getAllProjects() {
return ResourcesPlugin.getWorkspace().getRoot().getProjects();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,7 @@ public JavaProtocolExtensions getJavaExtensions() {
@Override
public CompletableFuture<List<? extends SymbolInformation>> symbol(WorkspaceSymbolParams params) {
logInfo(">> workspace/symbol");
WorkspaceSymbolHandler handler = new WorkspaceSymbolHandler();
WorkspaceSymbolHandler handler = new WorkspaceSymbolHandler(preferenceManager);
return computeAsync((monitor) -> {
return handler.search(params.getQuery(), monitor);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,58 +45,55 @@ public ReferencesHandler(PreferenceManager preferenceManager) {

private IJavaSearchScope createSearchScope() throws JavaModelException {
IJavaProject[] projects = JavaCore.create(ResourcesPlugin.getWorkspace().getRoot()).getJavaProjects();
return SearchEngine.createJavaSearchScope(projects, IJavaSearchScope.SOURCES | IJavaSearchScope.APPLICATION_LIBRARIES);
int scope = IJavaSearchScope.SOURCES;
if (preferenceManager.isClientSupportsClassFileContent()) {
scope |= IJavaSearchScope.APPLICATION_LIBRARIES;
}
return SearchEngine.createJavaSearchScope(projects, scope);
}

public List<Location> findReferences(ReferenceParams param, IProgressMonitor monitor) {

final List<Location> locations = new ArrayList<>();
try {
IJavaElement elementToSearch = JDTUtils.findElementAtSelection(JDTUtils.resolveTypeRoot(param.getTextDocument().getUri()),
param.getPosition().getLine(),
param.getPosition().getCharacter(), this.preferenceManager, monitor);
IJavaElement elementToSearch = JDTUtils.findElementAtSelection(JDTUtils.resolveTypeRoot(param.getTextDocument().getUri()), param.getPosition().getLine(), param.getPosition().getCharacter(), this.preferenceManager, monitor);

if(elementToSearch == null) {
if (elementToSearch == null) {
return locations;
}

boolean includeClassFiles = preferenceManager.isClientSupportsClassFileContent();
SearchEngine engine = new SearchEngine();
SearchPattern pattern = SearchPattern.createPattern(elementToSearch, IJavaSearchConstants.REFERENCES);
engine.search(pattern, new SearchParticipant[] { SearchEngine.getDefaultSearchParticipant() },
createSearchScope(), new SearchRequestor() {

engine.search(pattern, new SearchParticipant[] { SearchEngine.getDefaultSearchParticipant() }, createSearchScope(), new SearchRequestor() {

@Override
public void acceptSearchMatch(SearchMatch match) throws CoreException {
Object o = match.getElement();
if (o instanceof IJavaElement) {
IJavaElement element = (IJavaElement) o;
ICompilationUnit compilationUnit = (ICompilationUnit) element
.getAncestor(IJavaElement.COMPILATION_UNIT);
ICompilationUnit compilationUnit = (ICompilationUnit) element.getAncestor(IJavaElement.COMPILATION_UNIT);
Location location = null;
if (compilationUnit != null) {
location = JDTUtils.toLocation(compilationUnit, match.getOffset(),
match.getLength());
}
else{
location = JDTUtils.toLocation(compilationUnit, match.getOffset(), match.getLength());
} else if (includeClassFiles) {
IClassFile cf = (IClassFile) element.getAncestor(IJavaElement.CLASS_FILE);
if (cf != null && cf.getSourceRange() != null) {
location = JDTUtils.toLocation(cf, match.getOffset(), match.getLength());
}
}
if (location != null ) {
if (location != null) {
locations.add(location);
}
}
}
}, monitor);
}, monitor);

} catch (CoreException e) {
JavaLanguageServerPlugin.logException("Find references failure ", e);
}
return locations;
}

}



}
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,8 @@
import java.util.Collections;
import java.util.List;

import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jdt.core.Flags;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.search.IJavaSearchConstants;
import org.eclipse.jdt.core.search.IJavaSearchScope;
Expand All @@ -28,12 +25,19 @@
import org.eclipse.jdt.core.search.TypeNameMatchRequestor;
import org.eclipse.jdt.ls.core.internal.JDTUtils;
import org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin;
import org.eclipse.jdt.ls.core.internal.preferences.PreferenceManager;
import org.eclipse.lsp4j.Location;
import org.eclipse.lsp4j.SymbolInformation;
import org.eclipse.lsp4j.SymbolKind;

public class WorkspaceSymbolHandler{

private PreferenceManager preferenceManager;

public WorkspaceSymbolHandler(PreferenceManager preferenceManager) {
this.preferenceManager = preferenceManager;
}

public List<SymbolInformation> search(String query, IProgressMonitor monitor) {
if (query == null || query.trim().isEmpty()) {
return Collections.emptyList();
Expand Down Expand Up @@ -86,8 +90,7 @@ private SymbolKind mapKind(TypeNameMatch match) {
}

private IJavaSearchScope createSearchScope() throws JavaModelException {
IJavaProject[] projects = JavaCore.create(ResourcesPlugin.getWorkspace().getRoot()).getJavaProjects();
return SearchEngine.createJavaSearchScope(projects, IJavaSearchScope.SOURCES | IJavaSearchScope.APPLICATION_LIBRARIES | IJavaSearchScope.SYSTEM_LIBRARIES);
return JDTUtils.createSearchScope(null, preferenceManager);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -147,4 +147,8 @@ public boolean isProgressReportSupported() {
return Boolean.parseBoolean(extendedClientCapabilities.getOrDefault("progressReportProvider", "false").toString());
}

public boolean isClassFileContentSupported() {
return Boolean.parseBoolean(extendedClientCapabilities.getOrDefault("classFileContentsSupport", "false").toString());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import org.eclipse.jdt.core.formatter.DefaultCodeFormatterConstants;
import org.eclipse.jdt.core.manipulation.CodeStyleConfiguration;
import org.eclipse.jdt.internal.corext.util.CodeFormatterUtil;
import org.eclipse.jdt.ls.core.internal.IConstants;
import org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin;
import org.eclipse.jdt.ls.core.internal.StatusFactory;
import org.eclipse.jdt.ls.core.internal.corext.codemanipulation.CodeGenerationSettings;
Expand All @@ -53,23 +54,23 @@ public class PreferenceManager {
public PreferenceManager() {
preferences = new Preferences();
preferencesChangeListeners = new ListenerList<>();
eclipsePrefs = InstanceScope.INSTANCE.getNode(JavaLanguageServerPlugin.PLUGIN_ID);
eclipsePrefs = InstanceScope.INSTANCE.getNode(IConstants.PLUGIN_ID);
initialize();
}

/**
* Initialize default preference values of used bundles to match server
* functionality.
*/
public void initialize() {
public static void initialize() {
// Update JavaCore options
Hashtable<String, String> javaCoreOptions = JavaCore.getOptions();
javaCoreOptions.put(JavaCore.CODEASSIST_VISIBILITY_CHECK, JavaCore.ENABLED);
javaCoreOptions.put(DefaultCodeFormatterConstants.FORMATTER_USE_ON_OFF_TAGS, DefaultCodeFormatterConstants.TRUE);
JavaCore.setOptions(javaCoreOptions);

// Initialize default preferences
IEclipsePreferences defEclipsePrefs = DefaultScope.INSTANCE.getNode(JavaLanguageServerPlugin.PLUGIN_ID);
IEclipsePreferences defEclipsePrefs = DefaultScope.INSTANCE.getNode(IConstants.PLUGIN_ID);
defEclipsePrefs.put("org.eclipse.jdt.ui.typefilter.enabled", "");
defEclipsePrefs.put(CodeStyleConfiguration.ORGIMPORTS_IMPORTORDER, String.join(";", Preferences.JAVA_IMPORT_ORDER_DEFAULT));
defEclipsePrefs.put(CodeStyleConfiguration.ORGIMPORTS_ONDEMANDTHRESHOLD, "99");
Expand Down Expand Up @@ -206,4 +207,11 @@ public IEclipsePreferences getEclipsePreferences() {
return eclipsePrefs;
}

/**
* Checks whether the client supports class file contents
*/
public boolean isClientSupportsClassFileContent() {
return getClientPreferences() != null && getClientPreferences().isClassFileContentSupported();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ public class Inner {
/** Test */
public String test;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package org.sample;
import org.apache.commons.lang3.text.WordUtils;
public class TestJavadoc {

private String foo() {
Inner inner = new Inner();
return inner.test;
}

public class Inner {
/** Test */
public String test;
}

/**
* Uses {@link WordUtils}
*/
public void commonsLang() {

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public static String getURI(IProject project, String fqcn) throws JavaModelExcep
new SearchEngine().searchAllTypeNames(packageName.toCharArray(),SearchPattern.R_EXACT_MATCH,
className.toCharArray(), SearchPattern.R_EXACT_MATCH,
IJavaSearchConstants.TYPE,
JDTUtils.createSearchScope(javaProject),
JDTUtils.createSearchScope(javaProject, JavaLanguageServerPlugin.getPreferencesManager()),
extractor,
IJavaSearchConstants.WAIT_UNTIL_READY_TO_SEARCH,
new NullProgressMonitor());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1368,6 +1368,7 @@ private void mockLSP2Client() {

private void mockLSPClient(boolean isSnippetSupported, boolean isSignatureHelpSuported) {
reset(preferenceManager);
initPreferenceManager(true);
ClientPreferences mockCapabilies = mock(ClientPreferences.class);
// Mock the preference manager to use LSP v3 support.
when(preferenceManager.getClientPreferences()).thenReturn(mockCapabilies);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -521,6 +521,35 @@ public void testHoverOnJava10var() throws Exception {

}

@Test
public void testNoLinkWhenClassContentUnsupported() throws Exception {
initPreferenceManager(false);
testClassContentSupport("Uses WordUtils");
}

@Test
public void testLinkWhenClassContentSupported() throws Exception {
assertNotNull(DependencyUtil.getSources("org.apache.commons", "commons-lang3", "3.5"));
when(preferenceManager.isClientSupportsClassFileContent()).thenReturn(true);
testClassContentSupport("Uses \\[WordUtils\\]\\(jdt:.*\\)");
}

private void testClassContentSupport(String expectedJavadoc) throws Exception {
importProjects("maven/salut");
project = WorkspaceHelper.getProject("salut");
handler = new HoverHandler(preferenceManager);
//given
//Hovers on name.toUpperCase()
String payload = createHoverRequest("src/main/java/org/sample/TestJavadoc.java", 17, 20);
TextDocumentPositionParams position = getParams(payload);

//when
Hover hover = handler.hover(position, monitor);
assertNotNull(hover);
String javadoc = hover.getContents().get(1).getLeft();
assertMatches(expectedJavadoc, javadoc);
}

private String getTitleHover(ICompilationUnit cu, int line, int character) {
// when
Hover hover = getHover(cu, line, character);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import java.util.List;
Expand All @@ -23,8 +23,6 @@
import org.eclipse.jdt.ls.core.internal.ClassFileUtil;
import org.eclipse.jdt.ls.core.internal.WorkspaceHelper;
import org.eclipse.jdt.ls.core.internal.managers.AbstractProjectsManagerBasedTest;
import org.eclipse.jdt.ls.core.internal.preferences.PreferenceManager;
import org.eclipse.jdt.ls.core.internal.preferences.Preferences;
import org.eclipse.lsp4j.Location;
import org.eclipse.lsp4j.Position;
import org.eclipse.lsp4j.TextDocumentIdentifier;
Expand All @@ -40,12 +38,9 @@ public class NavigateToDefinitionHandlerTest extends AbstractProjectsManagerBase

private NavigateToDefinitionHandler handler;
private IProject project;
private PreferenceManager preferenceManager;

@Before
public void setUp() throws Exception {
preferenceManager = mock(PreferenceManager.class);
when(preferenceManager.getPreferences()).thenReturn(new Preferences());
handler = new NavigateToDefinitionHandler(preferenceManager);
importProjects("maven/salut");
project = WorkspaceHelper.getProject("salut");
Expand All @@ -65,6 +60,18 @@ public void testAttachedSource() throws Exception {
testClass("org.apache.commons.lang3.StringUtils", 20, 26);
}

@Test
public void testNoClassContentSupport() throws Exception {
when(preferenceManager.isClientSupportsClassFileContent()).thenReturn(false);
String uri = ClassFileUtil.getURI(project, "org.apache.commons.lang3.StringUtils");
List<? extends Location> definitions = handler.definition(new TextDocumentPositionParams(new TextDocumentIdentifier(uri), new Position(20, 26)), monitor);
assertNotNull(definitions);
assertEquals(1, definitions.size());
assertNull(definitions.get(0).getUri());
assertNull(definitions.get(0).getRange().getStart());
assertNull(definitions.get(0).getRange().getEnd());
}

@Test
public void testDisassembledSource() throws Exception {
testClass("javax.tools.Tool", 6, 44);
Expand Down

0 comments on commit fdfde47

Please sign in to comment.