Skip to content

Commit

Permalink
Allow build selected projects (#2138)
Browse files Browse the repository at this point in the history
- Add a new extended LSP request 'java/buildProjects' to support updating
  selected projects.

Signed-off-by: sheche <sheche@microsoft.com>
  • Loading branch information
jdneo authored Jun 29, 2022
1 parent 498ee9f commit a996b57
Show file tree
Hide file tree
Showing 7 changed files with 263 additions and 37 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.LinkedHashSet;
import java.util.LinkedList;
Expand All @@ -34,6 +35,7 @@
import org.codehaus.plexus.util.DirectoryScanner;
import org.eclipse.buildship.core.internal.configuration.GradleProjectNature;
import org.eclipse.core.resources.ICommand;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IProject;
Expand All @@ -58,6 +60,7 @@
import org.eclipse.jdt.ls.core.internal.managers.MavenProjectImporter;
import org.eclipse.jdt.ls.core.internal.managers.ProjectsManager;
import org.eclipse.jdt.ls.core.internal.preferences.PreferenceManager;
import org.eclipse.lsp4j.TextDocumentIdentifier;
import org.eclipse.m2e.core.internal.IMavenConstants;

/**
Expand Down Expand Up @@ -585,4 +588,40 @@ private static boolean isBuildFileMarker(IMarker marker, IProject project) {
return false;
}

/**
* Get a collection of projects based on the given document identifiers. The belonging projects of those
* documents will be added to the returned collection.
* @param identifiers a list of the {@link org.eclipse.lsp4j.TextDocumentIdentifier}
*/
public static Collection<IProject> getProjectsFromDocumentIdentifiers(List<TextDocumentIdentifier> identifiers) {
Set<IProject> projects = new HashSet<>();
for (TextDocumentIdentifier identifier : identifiers) {
IProject project = getProjectFromUri(identifier.getUri());
if (project != null) {
projects.add(project);
continue;
}
IFile file = JDTUtils.findFile(identifier.getUri());
if (file == null) {
continue;
}
project = file.getProject();
if (project != null) {
projects.add(project);
}
}
return projects;
}

private static IProject getProjectFromUri(String uri) {
IPath uriPath = ResourceUtils.canonicalFilePathFromURI(uri);
IProject[] projects = ResourcesPlugin.getWorkspace().getRoot().getProjects();
for (IProject project : projects) {
if (project.getLocation().equals(uriPath)) {
return project;
}
}
return null;
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2017 Microsoft Corporation and others.
* Copyright (c) 2017-2022 Microsoft Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
Expand All @@ -16,9 +16,14 @@
import static org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin.logException;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import org.eclipse.core.resources.IBuildConfiguration;
import org.eclipse.core.resources.ICommand;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
Expand All @@ -33,6 +38,7 @@
import org.eclipse.jdt.ls.core.internal.ProjectUtils;
import org.eclipse.jdt.ls.core.internal.ResourceUtils;
import org.eclipse.jdt.ls.core.internal.managers.ProjectsManager;
import org.eclipse.lsp4j.extended.ProjectBuildParams;

/**
* @author xuzho
Expand Down Expand Up @@ -85,6 +91,92 @@ public BuildWorkspaceStatus buildWorkspace(boolean forceReBuild, IProgressMonito
}
}

/**
* Build the belonging projects of the document identifiers in the <code>params</code>.
* @param params the project build parameter.
* @param monitor progress monitor.
* @return {@link org.eclipse.jdt.ls.core.internal.BuildWorkspaceStatus}
*/
public BuildWorkspaceStatus buildProjects(ProjectBuildParams params, IProgressMonitor monitor) {
Collection<IProject> projects = ProjectUtils.getProjectsFromDocumentIdentifiers(params.getIdentifiers());

if (projects.size() == 0) {
logError("Build projects fail: Cannot find projects from given uris.");
return BuildWorkspaceStatus.CANCELLED;
}

IBuildConfiguration[] configs = getBuildConfigurationsToBuild(projects);
try {
if (params.isFullBuild()) {
ResourcesPlugin.getWorkspace().build(configs, IncrementalProjectBuilder.CLEAN_BUILD, true, monitor);
ResourcesPlugin.getWorkspace().build(configs, IncrementalProjectBuilder.FULL_BUILD, true, monitor);
} else {
ResourcesPlugin.getWorkspace().build(configs, IncrementalProjectBuilder.INCREMENTAL_BUILD, true, monitor);
}
} catch (CoreException e) {
logException("Failed to build projects.", e);
return BuildWorkspaceStatus.FAILED;
} catch (OperationCanceledException e) {
return BuildWorkspaceStatus.CANCELLED;
}

for (IProject project : projects) {
if (project.equals(ProjectsManager.getDefaultProject())) {
continue;
}

try {
List<IMarker> markers = ResourceUtils.getErrorMarkers(project);
if (markers != null && markers.size() > 0) {
return BuildWorkspaceStatus.WITH_ERROR;
}
} catch (CoreException e) {
logException("Failed to get error markers from project: " + project.getName(), e);
return BuildWorkspaceStatus.FAILED;
}
}

return BuildWorkspaceStatus.SUCCEED;
}

/**
* return project build configs, which will be passed to the workspace for building.
* The Workspace is responsible for resolving references.
*/
protected IBuildConfiguration[] getBuildConfigurationsToBuild(Collection<IProject> projects) {
Set<IBuildConfiguration> configs = new HashSet<>();
for (IProject project : projects) {
if (project != null && hasBuilder(project)) {
try {
configs.add(project.getActiveBuildConfig());
} catch(CoreException e) {
logException("Failed to get build config.", e);
}
}
}
return configs.toArray(new IBuildConfiguration[configs.size()]);
}

/**
* Returns whether there are builders configured on the given project.
*
* @return <code>true</code> if it has builders,
* <code>false</code> if not, or if this couldn't be determined
*/
protected boolean hasBuilder(IProject project) {
if (!project.isAccessible())
return false;
try {
ICommand[] commands = project.getDescription().getBuildSpec();
if (commands.length > 0) {
return true;
}
} catch (CoreException e) {
logException("Failed to check project's builder.", e);
}
return false;
}

private static String convertMarker(IMarker marker) {
StringBuilder builder = new StringBuilder();
String message = marker.getAttribute(IMarker.MESSAGE, "<no message>");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@
import org.eclipse.lsp4j.WorkspaceEdit;
import org.eclipse.lsp4j.WorkspaceSymbolParams;
import org.eclipse.lsp4j.extended.ProjectConfigurationsUpdateParam;
import org.eclipse.lsp4j.extended.ProjectBuildParams;
import org.eclipse.lsp4j.jsonrpc.CompletableFutures;
import org.eclipse.lsp4j.jsonrpc.messages.Either;
import org.eclipse.lsp4j.jsonrpc.services.JsonDelegate;
Expand Down Expand Up @@ -870,6 +871,13 @@ public CompletableFuture<BuildWorkspaceStatus> buildWorkspace(Either<Boolean, bo
return computeAsyncWithClientProgress((monitor) -> handler.buildWorkspace(rebuild, monitor));
}

@Override
public CompletableFuture<BuildWorkspaceStatus> buildProjects(ProjectBuildParams params) {
logInfo(">> java/buildProjects");
BuildWorkspaceHandler handler = new BuildWorkspaceHandler(pm);
return computeAsyncWithClientProgress((monitor) -> handler.buildProjects(params, monitor));
}

/* (non-Javadoc)
* @see org.eclipse.lsp4j.services.WorkspaceService#didChangeWorkspaceFolders(DidChangeWorkspaceFoldersParams)
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,11 @@
package org.eclipse.jdt.ls.core.internal.handlers;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Collection;
import java.util.List;
import java.util.Set;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.IPath;
import org.eclipse.jdt.ls.core.internal.JDTUtils;
import org.eclipse.jdt.ls.core.internal.ResourceUtils;
import org.eclipse.jdt.ls.core.internal.ProjectUtils;
import org.eclipse.jdt.ls.core.internal.managers.ProjectsManager;
import org.eclipse.lsp4j.TextDocumentIdentifier;

Expand All @@ -45,22 +40,7 @@ public ProjectConfigurationUpdateHandler(ProjectsManager projectManager) {
* files that belong to some projects in the workspace.
*/
public void updateConfigurations(List<TextDocumentIdentifier> identifiers) {
Set<IProject> projects = new HashSet<>();
for (TextDocumentIdentifier identifier : identifiers) {
IProject project = getProjectFromUri(identifier.getUri());
if (project != null) {
projects.add(project);
continue;
}
IFile file = JDTUtils.findFile(identifier.getUri());
if (file == null) {
continue;
}
project = file.getProject();
if (project != null) {
projects.add(project);
}
}
Collection<IProject> projects = ProjectUtils.getProjectsFromDocumentIdentifiers(identifiers);

for (IProject project : projects) {
// most likely the handler is invoked intentionally by the user, that's why
Expand All @@ -72,15 +52,4 @@ public void updateConfigurations(List<TextDocumentIdentifier> identifiers) {
public void updateConfiguration(TextDocumentIdentifier param) {
updateConfigurations(Arrays.asList(param));
}

private IProject getProjectFromUri(String uri) {
IPath uriPath = ResourceUtils.canonicalFilePathFromURI(uri);
IProject[] projects = ResourcesPlugin.getWorkspace().getRoot().getProjects();
for (IProject project : projects) {
if (project.getLocation().equals(uriPath)) {
return project;
}
}
return null;
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2016-2019 Red Hat Inc. and others.
* Copyright (c) 2016-2022 Red Hat Inc. and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
Expand Down Expand Up @@ -43,6 +43,7 @@
import org.eclipse.lsp4j.TextDocumentIdentifier;
import org.eclipse.lsp4j.WorkspaceEdit;
import org.eclipse.lsp4j.extended.ProjectConfigurationsUpdateParam;
import org.eclipse.lsp4j.extended.ProjectBuildParams;
import org.eclipse.lsp4j.jsonrpc.messages.Either;
import org.eclipse.lsp4j.jsonrpc.services.JsonNotification;
import org.eclipse.lsp4j.jsonrpc.services.JsonRequest;
Expand Down Expand Up @@ -79,6 +80,9 @@ public interface JavaProtocolExtensions {
@JsonRequest
CompletableFuture<BuildWorkspaceStatus> buildWorkspace(Either<Boolean, boolean[]> forceReBuild);

@JsonRequest
CompletableFuture<BuildWorkspaceStatus> buildProjects(ProjectBuildParams params);

@JsonRequest
CompletableFuture<OverridableMethodsResponse> listOverridableMethods(CodeActionParams params);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/*******************************************************************************
* Copyright (c) 2022 Microsoft Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Microsoft Corporation - initial API and implementation
*******************************************************************************/

package org.eclipse.lsp4j.extended;

import java.util.List;

import org.eclipse.lsp4j.TextDocumentIdentifier;
import org.eclipse.lsp4j.jsonrpc.validation.NonNull;
import org.eclipse.lsp4j.util.Preconditions;
import org.eclipse.xtext.xbase.lib.Pure;
import org.eclipse.xtext.xbase.lib.util.ToStringBuilder;

@SuppressWarnings("all")
public class ProjectBuildParams {
/**
* The text document's identifiers.
*/
@NonNull
private List<TextDocumentIdentifier> identifiers;

/**
* Whether this is a full build or incremental build.
*/
private boolean isFullBuild;

public ProjectBuildParams() {
}

public ProjectBuildParams(@NonNull final List<TextDocumentIdentifier> identifiers, final boolean isFullBuild) {
this.identifiers = Preconditions.<List<TextDocumentIdentifier>>checkNotNull(identifiers, "identifiers");
this.isFullBuild = isFullBuild;
}

@Pure
@NonNull
public List<TextDocumentIdentifier> getIdentifiers() {
return identifiers;
}

public void setIdentifiers(@NonNull final List<TextDocumentIdentifier> identifiers) {
this.identifiers = Preconditions.<List<TextDocumentIdentifier>>checkNotNull(identifiers, "identifiers");
}

public boolean isFullBuild() {
return isFullBuild;
}

public void setFullBuild(boolean isFullBuild) {
this.isFullBuild = isFullBuild;
}

@Override
@Pure
public String toString() {
ToStringBuilder b = new ToStringBuilder(this);
b.add("identifiers", this.identifiers);
b.add("isFullBuild", this.isFullBuild);
return b.toString();
}

@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((identifiers == null) ? 0 : identifiers.hashCode());
result = prime * result + (isFullBuild ? 1231 : 1237);
return result;
}

@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
ProjectBuildParams other = (ProjectBuildParams) obj;
if (identifiers == null) {
if (other.identifiers != null)
return false;
} else if (!identifiers.equals(other.identifiers))
return false;
if (isFullBuild != other.isFullBuild)
return false;
return true;
}

}
Loading

0 comments on commit a996b57

Please sign in to comment.