Skip to content

Commit

Permalink
Revalidate open files in project when classpath updates.
Browse files Browse the repository at this point in the history
  • Loading branch information
ethan-vanderheijden authored and rgrunber committed Jun 7, 2024
1 parent 4c0e045 commit bb26c98
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 47 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,19 @@
import java.util.HashSet;
import java.util.Set;

import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.WorkspaceJob;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jdt.core.ElementChangedEvent;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IElementChangedListener;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaElementDelta;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.ls.core.internal.BuildWorkspaceStatus;
import org.eclipse.jdt.ls.core.internal.EventNotification;
import org.eclipse.jdt.ls.core.internal.EventType;
Expand All @@ -41,53 +42,66 @@
import org.eclipse.lsp4j.extended.ProjectBuildParams;

public class ClasspathUpdateHandler implements IElementChangedListener {

private final JavaClientConnection connection;
private final BaseDocumentLifeCycleHandler lifeCycleHandler;

public ClasspathUpdateHandler(JavaClientConnection client) {
this(client, null);
}

public ClasspathUpdateHandler(JavaClientConnection client, BaseDocumentLifeCycleHandler lifeCycleHandler) {
this.connection = client;
this.lifeCycleHandler = lifeCycleHandler;
}

@Override
public void elementChanged(ElementChangedEvent event) {
// Collect project names which have classpath changed.
Set<String> uris = processDelta(event.getDelta(), null);
if (connection != null && uris != null && !uris.isEmpty()) {
for (String uri : uris) {
Set<IJavaProject> projects = new HashSet<>();
processDelta(event.getDelta(), projects);
if (connection != null && projects != null && !projects.isEmpty()) {
for (IJavaProject javaProject : projects) {
String uri = ProjectUtils.getProjectRealFolder(javaProject.getProject()).toFile().toURI().toString();
PreferenceManager preferenceManager = JavaLanguageServerPlugin.getPreferencesManager();
if (preferenceManager.getPreferences().getNullAnalysisMode().equals(FeatureStatus.automatic)) {
IProject project = ProjectUtils.getProjectFromUri(uri);
if (project != null) {
IJavaProject javaProject = ProjectUtils.getJavaProject(project);
if (javaProject != null) {
WorkspaceJob job = new WorkspaceJob("Classpath Update Job") {
@Override
public IStatus runInWorkspace(IProgressMonitor monitor) {
if (!preferenceManager.getPreferences().updateAnnotationNullAnalysisOptions(javaProject, true) || !preferenceManager.getPreferences().isAutobuildEnabled()) {
// When the project's compiler options didn't change or auto build is disabled, rebuilding is not required.
return Status.OK_STATUS;
}
BuildWorkspaceHandler buildWorkspaceHandler = new BuildWorkspaceHandler(JavaLanguageServerPlugin.getProjectsManager());
ProjectBuildParams params = new ProjectBuildParams(Arrays.asList(new TextDocumentIdentifier(uri)), true);
BuildWorkspaceStatus status = buildWorkspaceHandler.buildProjects(params, monitor);
switch (status) {
case FAILED:
case WITH_ERROR:
return Status.error("error occurs during building project");
case SUCCEED:
return Status.OK_STATUS;
case CANCELLED:
return Status.CANCEL_STATUS;
default:
return Status.OK_STATUS;
}
if (!preferenceManager.getPreferences().isAutobuildEnabled()) {
if (lifeCycleHandler != null) {
for (ICompilationUnit unit : JavaCore.getWorkingCopies(null)) {
if (unit.getJavaProject().equals(javaProject)) {
try {
lifeCycleHandler.triggerValidation(unit);
} catch (JavaModelException e) {
JavaLanguageServerPlugin.logException("Failed to revalidate document after classpath change: " + unit.getPath(), e);
}
};
job.setPriority(Job.SHORT);
job.setRule(project);
job.schedule();
}
}
}
} else if (preferenceManager.getPreferences().getNullAnalysisMode().equals(FeatureStatus.automatic)) {
WorkspaceJob job = new WorkspaceJob("Classpath Update Job") {
@Override
public IStatus runInWorkspace(IProgressMonitor monitor) {
if (!preferenceManager.getPreferences().updateAnnotationNullAnalysisOptions(javaProject, true)) {
// When the project's compiler options didn't change, rebuilding is not required.
return Status.OK_STATUS;
}
BuildWorkspaceHandler buildWorkspaceHandler = new BuildWorkspaceHandler(JavaLanguageServerPlugin.getProjectsManager());
ProjectBuildParams params = new ProjectBuildParams(Arrays.asList(new TextDocumentIdentifier(uri)), true);
BuildWorkspaceStatus status = buildWorkspaceHandler.buildProjects(params, monitor);
switch (status) {
case FAILED:
case WITH_ERROR:
return Status.error("error occurs during building project");
case SUCCEED:
return Status.OK_STATUS;
case CANCELLED:
return Status.CANCEL_STATUS;
default:
return Status.OK_STATUS;
}
}
};
job.setPriority(Job.SHORT);
job.setRule(javaProject.getProject());
job.schedule();
}
EventNotification notification = new EventNotification().withType(EventType.ClasspathUpdated).withData(uri);
this.connection.sendEventNotification(notification);
Expand All @@ -103,32 +117,27 @@ public void removeElementChangeListener() {
JavaCore.removeElementChangedListener(this);
}

private Set<String> processDeltaChildren(IJavaElementDelta delta, Set<String> uris) {
private void processDeltaChildren(IJavaElementDelta delta, Set<IJavaProject> projects) {
for (IJavaElementDelta c : delta.getAffectedChildren()) {
uris = processDelta(c, uris);
processDelta(c, projects);
}
return uris;
}

private Set<String> processDelta(IJavaElementDelta delta, Set<String> uris) {
private void processDelta(IJavaElementDelta delta, Set<IJavaProject> projects) {
IJavaElement element = delta.getElement();
switch (element.getElementType()) {
case IJavaElement.JAVA_MODEL:
uris = processDeltaChildren(delta, uris);
processDeltaChildren(delta, projects);
break;
case IJavaElement.JAVA_PROJECT:
if (isClasspathChanged(delta.getFlags())) {
if (uris == null) {
uris = new HashSet<String>();
}
IJavaProject javaProject = (IJavaProject) element;
uris.add(ProjectUtils.getProjectRealFolder(javaProject.getProject()).toFile().toURI().toString());
projects.add(javaProject);
}
break;
default:
break;
}
return uris;
}

private boolean isClasspathChanged(int flags) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ public IStatus run(IProgressMonitor monitor) {
try {
workspaceDiagnosticsHandler = new WorkspaceDiagnosticsHandler(JDTLanguageServer.this.client, pm, preferenceManager.getClientPreferences(), documentLifeCycleHandler);
workspaceDiagnosticsHandler.addResourceChangeListener();
classpathUpdateHandler = new ClasspathUpdateHandler(JDTLanguageServer.this.client);
classpathUpdateHandler = new ClasspathUpdateHandler(JDTLanguageServer.this.client, documentLifeCycleHandler);
classpathUpdateHandler.addElementChangeListener();
SourceAttachUpdateHandler attachListener = new SourceAttachUpdateHandler(client);
attachListener.addElementChangeListener();
Expand Down

0 comments on commit bb26c98

Please sign in to comment.