diff --git a/extension/jdtls.ext/com.microsoft.gradle.bs.importer/src/com/microsoft/gradle/bs/importer/GradleBuildServerProjectImporter.java b/extension/jdtls.ext/com.microsoft.gradle.bs.importer/src/com/microsoft/gradle/bs/importer/GradleBuildServerProjectImporter.java index 77dd302d6..f81bd9611 100644 --- a/extension/jdtls.ext/com.microsoft.gradle.bs.importer/src/com/microsoft/gradle/bs/importer/GradleBuildServerProjectImporter.java +++ b/extension/jdtls.ext/com.microsoft.gradle.bs.importer/src/com/microsoft/gradle/bs/importer/GradleBuildServerProjectImporter.java @@ -24,6 +24,7 @@ import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.SubMonitor; import org.eclipse.jdt.ls.core.internal.AbstractProjectImporter; import org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin; import org.eclipse.jdt.ls.core.internal.ProjectUtils; @@ -83,6 +84,15 @@ public boolean applies(IProgressMonitor monitor) throws OperationCanceledExcepti directories = gradleDetector.scan(monitor); } + for (java.nio.file.Path directory : directories) { + IProject project = ProjectUtils.getProjectFromUri(directory.toUri().toString()); + // skip this importer if any of the project in workspace is already + // imported by other importers. + if (project != null && !Utils.isGradleBuildServerProject(project)) { + return false; + } + } + return !directories.isEmpty(); } @@ -211,13 +221,17 @@ private IProject createProject(File directory, IProgressMonitor monitor) throws } private void updateProjectDescription(IProject project, IProgressMonitor monitor) throws CoreException { + SubMonitor progress = SubMonitor.convert(monitor, 1); IProjectDescription projectDescription = project.getDescription(); + Utils.removeBuildshipConfigurations(projectDescription); + ICommand problemReporter = projectDescription.newCommand(); problemReporter.setBuilderName(JavaProblemChecker.BUILDER_ID); - Utils.addBuildSpec(project, new ICommand[] { + Utils.addBuildSpec(projectDescription, new ICommand[] { Utils.getBuildServerBuildSpec(projectDescription), problemReporter - }, monitor); + }); + project.setDescription(projectDescription, IResource.AVOID_NATURE_CONFIG, progress.newChild(1)); // Here we don't use the public API: {@code project.setDescription()} to update the project, // because that API will ignore the variable descriptions. @@ -254,19 +268,6 @@ private BuildServerPreferences getBuildServerPreferences() { private boolean isBuildServerEnabled() { Preferences preferences = getPreferences(); String bspImporterEnabled = getString(preferences.asMap(), JAVA_BUILD_SERVER_GRADLE_ENABLED); - if (bspImporterEnabled == null) { - return false; - } - - if ("on".equalsIgnoreCase(bspImporterEnabled)) { - return true; - } else if ("auto".equalsIgnoreCase(bspImporterEnabled)){ - // Rely on the workspace storage path to determine if the client is VSCode Insiders. - // This may not always true if user changes the storage path manually, but should - // work in most cases. - return ResourcesPlugin.getPlugin().getStateLocation().toString().contains("Code - Insiders"); - } - - return false; + return "on".equalsIgnoreCase(bspImporterEnabled); } } diff --git a/extension/jdtls.ext/com.microsoft.gradle.bs.importer/src/com/microsoft/gradle/bs/importer/Utils.java b/extension/jdtls.ext/com.microsoft.gradle.bs.importer/src/com/microsoft/gradle/bs/importer/Utils.java index db6126b4e..308130373 100644 --- a/extension/jdtls.ext/com.microsoft.gradle.bs.importer/src/com/microsoft/gradle/bs/importer/Utils.java +++ b/extension/jdtls.ext/com.microsoft.gradle.bs.importer/src/com/microsoft/gradle/bs/importer/Utils.java @@ -18,6 +18,7 @@ import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.SubMonitor; import org.eclipse.core.runtime.URIUtil; +import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.ls.core.internal.ProjectUtils; import com.microsoft.gradle.bs.importer.builder.BuildServerBuilder; @@ -115,36 +116,28 @@ public static void addNature(IProject project, String natureId, IProgressMonitor } /** - * Add the builders to the project if the input builders do not exist in project description. + * Add the builders to the project description if the input builders do not exist in description. * All the configuration of the builder will be set to default. * - * @param project the project to add the builder to. + * @param project the project description. * @param buildNames the names of the builder. - * @param monitor the progress monitor. - * @throws CoreException */ - public static void addBuildSpec(IProject project, String[] buildNames, IProgressMonitor monitor) throws CoreException { - IProjectDescription description = project.getDescription(); + public static void addBuildSpec(IProjectDescription description, String[] buildNames) { ICommand[] commands = Arrays.stream(buildNames).map(buildName -> { ICommand buildSpec = description.newCommand(); buildSpec.setBuilderName(buildName); return buildSpec; }).toArray(ICommand[]::new); - addBuildSpec(project, commands, monitor); + addBuildSpec(description, commands); } /** - * Add the builders to the project if the input builders do not exist in project description. + * Add the builders to the project description if the input builders do not exist in description. * - * @param project the project to add the builder to. + * @param project the project description. * @param buildSpecs the builders to add. - * @param monitor the progress monitor. - * @throws CoreException */ - public static void addBuildSpec(IProject project, ICommand[] buildSpecs, IProgressMonitor monitor) throws CoreException { - SubMonitor progress = SubMonitor.convert(monitor, 1); - // get the description - IProjectDescription description = project.getDescription(); + public static void addBuildSpec(IProjectDescription description, ICommand[] buildSpecs) { List currentBuildSpecs = Arrays.asList(description.getBuildSpec()); List newSpecs = new LinkedList<>(); newSpecs.addAll(currentBuildSpecs); @@ -152,16 +145,38 @@ public static void addBuildSpec(IProject project, ICommand[] buildSpecs, IProgre if (currentBuildSpecs.stream().anyMatch(spec -> Objects.equals(spec.getBuilderName(), buildSpec.getBuilderName()))) { continue; } - newSpecs.add(0, buildSpec); } - if (newSpecs.size() == currentBuildSpecs.size()) { - return; - } + description.setBuildSpec(newSpecs.toArray(new ICommand[newSpecs.size()])); + } + /** + * Remove the builders and natures that are introduced by Buildship. + */ + public static void removeBuildshipConfigurations(IProjectDescription description) { + ICommand[] commands = description.getBuildSpec(); + List newSpecs = new LinkedList<>(); + for (ICommand command : commands) { + if (!isExcludedBuilder(command.getBuilderName())) { + newSpecs.add(command); + } + } description.setBuildSpec(newSpecs.toArray(new ICommand[newSpecs.size()])); - project.setDescription(description, IResource.AVOID_NATURE_CONFIG, progress.newChild(1)); + + String[] natureIds = description.getNatureIds(); + List newNatureIds = new LinkedList<>(); + for (String natureId : natureIds) { + if (!Objects.equals(natureId, "org.eclipse.buildship.core.gradleprojectnature")) { + newNatureIds.add(natureId); + } + } + description.setNatureIds(newNatureIds.toArray(new String[newNatureIds.size()])); + } + + private static boolean isExcludedBuilder(String builderName) { + return Objects.equals(builderName, JavaCore.BUILDER_ID) + || Objects.equals(builderName, "org.eclipse.buildship.core.gradleprojectbuilder"); } /** diff --git a/extension/package.json b/extension/package.json index 17408204e..22dc23470 100644 --- a/extension/package.json +++ b/extension/package.json @@ -831,12 +831,11 @@ "java.gradle.buildServer.enabled": { "type": "string", "enum": [ - "auto", "on", "off" ], - "markdownDescription": "[Experimental] Use build server to synchronize Gradle project when enabled. When set to `auto`, the build server will be enabled in Visual Studio Code - Insiders.", - "default": "auto" + "markdownDescription": "Whether to use build server to synchronize Gradle project. It will replace the original Buildship to import the Gradle when enabled.", + "default": "on" }, "java.gradle.buildServer.openBuildOutput": { "type": "string", diff --git a/extension/src/bs/BuildServerController.ts b/extension/src/bs/BuildServerController.ts index 9cfafaf36..c19d3718a 100644 --- a/extension/src/bs/BuildServerController.ts +++ b/extension/src/bs/BuildServerController.ts @@ -1,7 +1,21 @@ -import { Disposable, ExtensionContext, OutputChannel, commands, languages, window } from "vscode"; +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +import { + ConfigurationChangeEvent, + Disposable, + ExtensionContext, + OutputChannel, + commands, + languages, + window, + workspace, +} from "vscode"; import { GradleBuildLinkProvider } from "./GradleBuildLinkProvider"; import { sendInfo } from "vscode-extension-telemetry-wrapper"; import { OpenBuildOutputValue, getOpenBuildOutput } from "../util/config"; +import * as path from "path"; +import * as fse from "fs-extra"; const APPEND_BUILD_LOG_CMD = "_java.gradle.buildServer.appendBuildLog"; const LOG_CMD = "_java.gradle.buildServer.log"; @@ -48,6 +62,29 @@ export class BuildServerController implements Disposable { commands.registerCommand(SEND_TELEMETRY_CMD, (jsonString: string) => { const log = JSON.parse(jsonString); sendInfo("", log); + }), + workspace.onDidChangeConfiguration((e: ConfigurationChangeEvent) => { + if (e.affectsConfiguration("java.gradle.buildServer.enabled")) { + const storagePath = context.storageUri?.fsPath; + if (!storagePath) { + return; + } + + const msg = + "Please reload to make the change of 'java.gradle.buildServer.enabled' take effect. Reload now?"; + const action = "Reload"; + window.showWarningMessage(msg, action).then(async (selection) => { + if (action === selection) { + // generate a flag file to make it a clean reload. + // https://github.com/redhat-developer/vscode-java/blob/d02cf8ecfee1f3f528770a51ada825d522356967/src/settings.ts#L46 + const jlsWorkspacePath = path.resolve(storagePath, "..", "redhat.java", "jdt_ws"); + await fse.ensureDir(jlsWorkspacePath); + const flagFile = path.resolve(jlsWorkspacePath, ".cleanWorkspace"); + await fse.writeFile(flagFile, ""); + commands.executeCommand("workbench.action.reloadWindow"); + } + }); + } }) ); } diff --git a/extension/src/bs/GradleBuildLinkProvider.ts b/extension/src/bs/GradleBuildLinkProvider.ts index bfa63dd27..858e155bd 100644 --- a/extension/src/bs/GradleBuildLinkProvider.ts +++ b/extension/src/bs/GradleBuildLinkProvider.ts @@ -1,3 +1,6 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + import { DocumentLink, DocumentLinkProvider, ProviderResult, Range, TextDocument, Uri } from "vscode"; export class GradleBuildLinkProvider implements DocumentLinkProvider {