jvmTargets = new ArrayList<>();
+ for (BuildTarget buildTarget : buildTargets) {
+ JvmBuildTargetExt model = JSONUtility.toModel(buildTarget.getData(), JvmBuildTargetExt.class);
+ if (StringUtils.isBlank(model.getTargetBytecodeVersion()) || StringUtils.isBlank(model.getJavaHome())) {
+ continue;
+ }
+ jvmTargets.add(JSONUtility.toModel(buildTarget.getData(), JvmBuildTargetExt.class));
+ }
+
+ if (jvmTargets.isEmpty()) {
+ throw new CoreException(new Status(IStatus.ERROR, BuildServerAdapter.PLUGIN_ID, "Failed to get Java version."));
+ }
+
+ jvmTargets.sort((j1, j2) -> {
+ String[] components1 = j1.getTargetBytecodeVersion().split("\\.");
+ String[] components2 = j2.getTargetBytecodeVersion().split("\\.");
+
+ int length = Math.max(components1.length, components2.length);
+ for (int i = 0; i < length; i++) {
+ int version1 = i < components1.length ? Integer.parseInt(components1[i]) : 0;
+ int version2 = i < components2.length ? Integer.parseInt(components2[i]) : 0;
+
+ if (version1 != version2) {
+ return Integer.compare(version1, version2);
+ }
+ }
+
+ return 0; // versions are equal
+ });
+
+ return jvmTargets.get(jvmTargets.size() - 1);
+ }
+
+ @Override
+ public boolean fileChanged(IResource resource, CHANGE_TYPE changeType, IProgressMonitor monitor) throws CoreException {
+ if (resource == null || !applies(resource.getProject())) {
+ return false;
+ }
+ return IBuildSupport.super.fileChanged(resource, changeType, monitor) || isBuildFile(resource);
+ }
+
+ @Override
+ public boolean isBuildFile(IResource resource) {
+ if (resource != null && resource.getType() == IResource.FILE && isBuildLikeFileName(resource.getName())
+ && ProjectUtils.hasNature(resource.getProject(), BspGradleProjectNature.NATURE_ID)) {
+ try {
+ if (!ProjectUtils.isJavaProject(resource.getProject())) {
+ return true;
+ }
+ IJavaProject javaProject = JavaCore.create(resource.getProject());
+ IPath outputLocation = javaProject.getOutputLocation();
+ return outputLocation == null || !outputLocation.isPrefixOf(resource.getFullPath());
+ } catch (JavaModelException e) {
+ JavaLanguageServerPlugin.logException(e.getMessage(), e);
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean isBuildLikeFileName(String fileName) {
+ return GRADLE_FILE_EXT.matcher(fileName).matches() || fileName.equals(GRADLE_PROPERTIES);
+ }
+
+ /**
+ * Get the Eclipse compatible Java version string.
+ *
+ * See:
+ org.eclipse.buildship.core.internal.util.gradle.JavaVersionUtil
+ *
+ */
+ String getEclipseCompatibleVersion(String javaVersion) {
+ if ("1.9".equals(javaVersion)) {
+ return "9";
+ } else if ("1.10".equals(javaVersion)) {
+ return "10";
+ }
+
+ return javaVersion;
+ }
+
+ private void addProjectDependenciesToClasspath(List classpath, Set projectDependencies) {
+ for (BuildTargetIdentifier dependency : projectDependencies) {
+ String uriString = dependency.getUri();
+ URI uri = null;
+ // TODO: extract to util
+ try {
+ uri = new URI(uriString);
+ uri = new URI(uri.getScheme(), uri.getHost(), uri.getPath(), null, uri.getFragment());
+ } catch (URISyntaxException e) {
+ e.printStackTrace();
+ }
+ if (uri != null) {
+ IProject dependencyProject = ProjectUtils.getProjectFromUri(uri.toString());
+ if (dependencyProject != null) {
+ classpath.add(JavaCore.newProjectEntry(dependencyProject.getFullPath()));
+ }
+ }
+ }
+ }
+
+ private void addModuleDependenciesToClasspath(List classpath, Set modules, boolean isTest) {
+ for (MavenDependencyModule mainDependency : modules) {
+ File artifact = null;
+ File sourceArtifact = null;
+ for (MavenDependencyModuleArtifact a : mainDependency.getArtifacts()) {
+ try {
+ if (a.getClassifier() == null) {
+ artifact = new File(new URI(a.getUri()));
+ } else if ("sources".equals(a.getClassifier())) {
+ sourceArtifact = new File(new URI(a.getUri()));
+ }
+ } catch (URISyntaxException e) {
+ e.printStackTrace();
+ }
+ }
+
+ List attributes = new LinkedList<>();
+ if (isTest) {
+ attributes.add(testAttribute);
+ }
+ if (!artifact.exists()) {
+ attributes.add(optionalAttribute);
+ }
+
+ classpath.add(JavaCore.newLibraryEntry(
+ new org.eclipse.core.runtime.Path(artifact.getAbsolutePath()),
+ sourceArtifact == null ? null : new org.eclipse.core.runtime.Path(sourceArtifact.getAbsolutePath()),
+ null,
+ ClasspathEntry.NO_ACCESS_RULES,
+ attributes.size() == 0 ? ClasspathEntry.NO_EXTRA_ATTRIBUTES : attributes.toArray(new IClasspathAttribute[attributes.size()]),
+ false
+ ));
+ }
+ }
+
+ private void addJavaNature(IProject project, IProgressMonitor monitor) throws CoreException {
+ SubMonitor progress = SubMonitor.convert(monitor, 1);
+ // get the description
+ IProjectDescription description = project.getDescription();
+
+ // abort if the project already has the nature applied or the nature is not defined
+ List currentNatureIds = Arrays.asList(description.getNatureIds());
+ if (currentNatureIds.contains(JavaCore.NATURE_ID)) {
+ return;
+ }
+
+ // add the nature to the project
+ List newIds = new LinkedList<>();
+ newIds.addAll(currentNatureIds);
+ newIds.add(0, JavaCore.NATURE_ID);
+ description.setNatureIds(newIds.toArray(new String[newIds.size()]));
+
+ // save the updated description
+ project.setDescription(description, IResource.AVOID_NATURE_CONFIG, progress.newChild(1));
+ }
+}
+
diff --git a/jdtls.ext/com.microsoft.buildserver.adapter/src/com/microsoft/buildserver/adapter/BspGradleProjectImporter.java b/jdtls.ext/com.microsoft.buildserver.adapter/src/com/microsoft/buildserver/adapter/BspGradleProjectImporter.java
new file mode 100644
index 00000000..df468fd2
--- /dev/null
+++ b/jdtls.ext/com.microsoft.buildserver.adapter/src/com/microsoft/buildserver/adapter/BspGradleProjectImporter.java
@@ -0,0 +1,285 @@
+package com.microsoft.buildserver.adapter;
+
+import static org.eclipse.jdt.ls.core.internal.handlers.MapFlattener.getValue;
+
+import java.io.File;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.nio.file.Path;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Optional;
+
+import org.eclipse.core.resources.ICommand;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IProjectDescription;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IWorkspace;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jdt.ls.core.internal.AbstractProjectImporter;
+import org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin;
+import org.eclipse.jdt.ls.core.internal.ProjectUtils;
+import org.eclipse.jdt.ls.core.internal.managers.BasicFileDetector;
+import org.eclipse.jdt.ls.core.internal.preferences.Preferences;
+
+import org.eclipse.lsp4j.Command;
+import org.eclipse.lsp4j.ExecuteCommandParams;
+import org.eclipse.lsp4j.MessageType;
+
+import com.microsoft.buildserver.adapter.builder.BspBuilder;
+
+import ch.epfl.scala.bsp4j.BuildClientCapabilities;
+import ch.epfl.scala.bsp4j.BuildServer;
+import ch.epfl.scala.bsp4j.BuildTarget;
+import ch.epfl.scala.bsp4j.InitializeBuildParams;
+import ch.epfl.scala.bsp4j.InitializeBuildResult;
+import ch.epfl.scala.bsp4j.WorkspaceBuildTargetsResult;
+
+@SuppressWarnings("restriction")
+public class BspGradleProjectImporter extends AbstractProjectImporter {
+
+ private static final String JAVA_BUILD_SERVER_GRADLE_ENABLED = "java.buildServer.gradle.enabled";
+ public static final String BUILD_GRADLE_DESCRIPTOR = "build.gradle";
+ public static final String BUILD_GRADLE_KTS_DESCRIPTOR = "build.gradle.kts";
+ public static final String SETTINGS_GRADLE_DESCRIPTOR = "settings.gradle";
+ public static final String SETTINGS_GRADLE_KTS_DESCRIPTOR = "settings.gradle.kts";
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jdt.ls.core.internal.managers.IProjectImporter#applies(org.eclipse.core.runtime.IProgressMonitor)
+ */
+ @Override
+ public boolean applies(IProgressMonitor monitor) throws CoreException {
+ if (rootFolder == null) {
+ return false;
+ }
+
+ Preferences preferences = getPreferences();
+ if (!preferences.isImportGradleEnabled()) {
+ return false;
+ }
+
+ Object bspImporterEnabled = getValue(preferences.asMap(), JAVA_BUILD_SERVER_GRADLE_ENABLED);
+ if (bspImporterEnabled == null) {
+ return false;
+ }
+
+ if (!(boolean) bspImporterEnabled) {
+ return false;
+ }
+
+ if (directories == null) {
+ BasicFileDetector gradleDetector = new BasicFileDetector(rootFolder.toPath(), BUILD_GRADLE_DESCRIPTOR,
+ SETTINGS_GRADLE_DESCRIPTOR, BUILD_GRADLE_KTS_DESCRIPTOR, SETTINGS_GRADLE_KTS_DESCRIPTOR)
+ .includeNested(false)
+ .addExclusions("**/build") //default gradle build dir
+ .addExclusions("**/bin");
+ directories = gradleDetector.scan(monitor);
+ }
+
+ for (Path directory : directories) {
+ IProject project = ProjectUtils.getProjectFromUri(directory.toUri().toString());
+ if (project == null) {
+ return true;
+ }
+
+ if (BspUtils.isBspGradleProject(project)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jdt.ls.core.internal.managers.IProjectImporter#importToWorkspace(org.eclipse.core.runtime.IProgressMonitor)
+ */
+ @Override
+ public void importToWorkspace(IProgressMonitor monitor) throws CoreException {
+ BuildServer buildServer = BuildServerAdapter.getBuildServer();
+ if (buildServer == null) {
+ return;
+ }
+
+ if (BuildServerTargetsManager.getInstance().getInitializeBuildResult(rootFolder) == null) {
+ InitializeBuildParams params = new InitializeBuildParams(
+ Constant.CLIENT_NAME,
+ Constant.CLIENT_VERSION,
+ Constant.BSP_VERSION,
+ rootFolder.toPath().toUri().toString(),
+ new BuildClientCapabilities(java.util.Collections.singletonList("java"))
+ );
+ BuildServerPreferences data = getBuildServerPreferences();
+ params.setData(data);
+ try {
+ InitializeBuildResult initializeResult = buildServer.buildInitialize(params).join();
+ buildServer.onBuildInitialized();
+ BuildServerTargetsManager.getInstance().setInitializeBuildResult(rootFolder, initializeResult);
+ } catch (Exception e) {
+ JavaLanguageServerPlugin.getInstance().getClientConnection().sendActionableNotification(
+ MessageType.Error,
+ "Failed to initialize the build server: " + e.getMessage(),
+ null,
+ Arrays.asList(new Command("Open Log", "java.buildServer.openLogs"))
+ );
+ }
+ }
+
+ WorkspaceBuildTargetsResult workspaceBuildTargetsResult = buildServer.workspaceBuildTargets().join();
+ List buildTargets = workspaceBuildTargetsResult.getTargets();
+
+ List projects = createProjectsIfNotExist(buildTargets, monitor);
+ if (projects.isEmpty()) {
+ return;
+ }
+
+ // store the digest for the imported gradle projects.
+ ProjectUtils.getGradleProjects().forEach(p -> {
+ File buildFile = p.getFile(BUILD_GRADLE_DESCRIPTOR).getLocation().toFile();
+ File settingsFile = p.getFile(SETTINGS_GRADLE_DESCRIPTOR).getLocation().toFile();
+ File buildKtsFile = p.getFile(BUILD_GRADLE_KTS_DESCRIPTOR).getLocation().toFile();
+ File settingsKtsFile = p.getFile(SETTINGS_GRADLE_KTS_DESCRIPTOR).getLocation().toFile();
+ try {
+ if (buildFile.exists()) {
+ BuildServerAdapter.getDigestStore().updateDigest(buildFile.toPath());
+ } else if (buildKtsFile.exists()) {
+ BuildServerAdapter.getDigestStore().updateDigest(buildKtsFile.toPath());
+ }
+ if (settingsFile.exists()) {
+ BuildServerAdapter.getDigestStore().updateDigest(settingsFile.toPath());
+ } else if (settingsKtsFile.exists()) {
+ BuildServerAdapter.getDigestStore().updateDigest(settingsKtsFile.toPath());
+ }
+ } catch (CoreException e) {
+ JavaLanguageServerPlugin.logException("Failed to update digest for gradle build file", e);
+ }
+ });
+
+ BspGradleBuildSupport bs = new BspGradleBuildSupport();
+
+
+ if (!projects.isEmpty()) {
+ Preferences preferences = getPreferences();
+ if (preferences.isAutobuildEnabled()) {
+ JavaLanguageServerPlugin.getInstance().getClientConnection().sendNotification("_java.buildServer.configAutoBuild", Collections.emptyList());
+ }
+ }
+
+ for (IProject project : projects) {
+ // separate the classpath update and project dependency update to avoid
+ // having Java project 'xxx' does not exists.
+ // TODO: consider to use a better way to handle this: i.e.
+ // add java nature for all java projects first.
+ bs.updateClassPath(project, false, monitor);
+ }
+ for (IProject project : projects) {
+ bs.addProjectDependencies(project, monitor);
+ }
+ }
+
+ private List createProjectsIfNotExist(List buildTargets, IProgressMonitor monitor) throws CoreException {
+ List projects = new LinkedList<>();
+ Map> buildTargetMap = BspUtils.mapBuildTargetsByUri(buildTargets);
+ for (Entry> entrySet : buildTargetMap.entrySet()) {
+ String baseDir = entrySet.getKey();
+ if (baseDir == null) {
+ JavaLanguageServerPlugin.logError("The base directory of the build target is null.");
+ continue;
+ }
+ File projectDirectory;
+ try {
+ projectDirectory = new File(new URI(baseDir));
+ } catch (URISyntaxException e) {
+ JavaLanguageServerPlugin.logException(e);
+ continue;
+ }
+ IProject[] allProjects = ProjectUtils.getAllProjects();
+ Optional projectOrNull = Arrays.stream(allProjects).filter(p -> {
+ File loc = p.getLocation().toFile();
+ return loc.equals(projectDirectory);
+ }).findFirst();
+
+ IProject project;
+ if (projectOrNull.isPresent()) {
+ project = projectOrNull.get();
+ } else {
+ String projectName = findFreeProjectName(projectDirectory.getName());
+ IWorkspace workspace = ResourcesPlugin.getWorkspace();
+ IProjectDescription projectDescription = workspace.newProjectDescription(projectName);
+ projectDescription.setLocation(org.eclipse.core.runtime.Path.fromOSString(projectDirectory.getPath()));
+ projectDescription.setNatureIds(new String[]{BspGradleProjectNature.NATURE_ID});
+ ICommand buildSpec = projectDescription.newCommand();
+ buildSpec.setBuilderName(BspBuilder.BUILDER_ID);
+ projectDescription.setBuildSpec(new ICommand[]{buildSpec});
+
+ project = workspace.getRoot().getProject(projectName);
+ project.create(projectDescription, monitor);
+
+ // open the project
+ project.open(IResource.NONE, monitor);
+
+ }
+
+ if (project == null || !project.isAccessible()) {
+ continue;
+ }
+
+ project.refreshLocal(IResource.DEPTH_INFINITE, monitor);
+ projects.add(project);
+ BuildServerTargetsManager.getInstance().setBuildTargets(project, entrySet.getValue());
+ }
+ return projects;
+ }
+
+ @Override
+ public void reset() {
+ // do nothing.
+ }
+
+ private BuildServerPreferences getBuildServerPreferences() {
+ BuildServerPreferences data = new BuildServerPreferences();
+ Preferences jdtlsPreferences = getPreferences();
+ data.setGradleArguments(jdtlsPreferences.getGradleArguments());
+ data.setGradleHome(jdtlsPreferences.getGradleHome());
+ data.setGradleJavaHome(jdtlsPreferences.getGradleJavaHome());
+ data.setGradleJvmArguments(jdtlsPreferences.getGradleJvmArguments());
+ data.setGradleUserHome(jdtlsPreferences.getGradleUserHome());
+ data.setGradleVersion(jdtlsPreferences.getGradleVersion());
+ data.setGradleWrapperEnabled(jdtlsPreferences.isGradleWrapperEnabled());
+ return data;
+ }
+
+ private String findFreeProjectName(String baseName) {
+ IProject project = Arrays.stream(ProjectUtils.getAllProjects())
+ .filter(p -> p.getName().equals(baseName)).findFirst().orElse(null);
+ return project != null ? findFreeProjectName(baseName + "_") : baseName;
+ }
+
+ private void addJavaNature(IProject project, IProgressMonitor monitor) throws CoreException {
+ SubMonitor progress = SubMonitor.convert(monitor, 1);
+ // get the description
+ IProjectDescription description = project.getDescription();
+
+ // abort if the project already has the nature applied or the nature is not defined
+ List currentNatureIds = Arrays.asList(description.getNatureIds());
+ if (currentNatureIds.contains(JavaCore.NATURE_ID)) {
+ return;
+ }
+
+ // add the nature to the project
+ List newIds = new LinkedList<>();
+ newIds.addAll(currentNatureIds);
+ newIds.add(0, JavaCore.NATURE_ID);
+ description.setNatureIds(newIds.toArray(new String[newIds.size()]));
+
+ // save the updated description
+ project.setDescription(description, IResource.AVOID_NATURE_CONFIG, progress.newChild(1));
+ }
+}
+
diff --git a/jdtls.ext/com.microsoft.buildserver.adapter/src/com/microsoft/buildserver/adapter/BspGradleProjectNature.java b/jdtls.ext/com.microsoft.buildserver.adapter/src/com/microsoft/buildserver/adapter/BspGradleProjectNature.java
new file mode 100644
index 00000000..b7108952
--- /dev/null
+++ b/jdtls.ext/com.microsoft.buildserver.adapter/src/com/microsoft/buildserver/adapter/BspGradleProjectNature.java
@@ -0,0 +1,32 @@
+package com.microsoft.buildserver.adapter;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IProjectNature;
+import org.eclipse.core.runtime.CoreException;
+
+public class BspGradleProjectNature implements IProjectNature {
+ public static final String NATURE_ID = "com.microsoft.buildserver.adapter.bspGradleProjectNature";
+
+ private IProject project;
+
+ @Override
+ public void configure() throws CoreException {
+
+ }
+
+ @Override
+ public void deconfigure() throws CoreException {
+
+ }
+
+ @Override
+ public IProject getProject() {
+ return project;
+ }
+
+ @Override
+ public void setProject(IProject project) {
+ this.project = project;
+ }
+}
+
diff --git a/jdtls.ext/com.microsoft.buildserver.adapter/src/com/microsoft/buildserver/adapter/BspUtils.java b/jdtls.ext/com.microsoft.buildserver.adapter/src/com/microsoft/buildserver/adapter/BspUtils.java
new file mode 100644
index 00000000..0afae370
--- /dev/null
+++ b/jdtls.ext/com.microsoft.buildserver.adapter/src/com/microsoft/buildserver/adapter/BspUtils.java
@@ -0,0 +1,29 @@
+package com.microsoft.buildserver.adapter;
+
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.jdt.ls.core.internal.ProjectUtils;
+
+import ch.epfl.scala.bsp4j.BuildTarget;
+
+public class BspUtils {
+ private BspUtils() {}
+
+ public static Map> mapBuildTargetsByUri(List buildTargets) {
+ return buildTargets.stream().collect(Collectors.groupingBy(target -> {
+ String uri = target.getId().getUri();
+ int indexOfQuery = uri.indexOf("?");
+ if (indexOfQuery != -1) {
+ uri = uri.substring(0, indexOfQuery);
+ }
+ return uri;
+ }));
+ }
+
+ public static boolean isBspGradleProject(IProject project) {
+ return ProjectUtils.hasNature(project, BspGradleProjectNature.NATURE_ID);
+ }
+}
diff --git a/jdtls.ext/com.microsoft.buildserver.adapter/src/com/microsoft/buildserver/adapter/BuildServerAdapter.java b/jdtls.ext/com.microsoft.buildserver.adapter/src/com/microsoft/buildserver/adapter/BuildServerAdapter.java
new file mode 100644
index 00000000..723367a1
--- /dev/null
+++ b/jdtls.ext/com.microsoft.buildserver.adapter/src/com/microsoft/buildserver/adapter/BuildServerAdapter.java
@@ -0,0 +1,135 @@
+package com.microsoft.buildserver.adapter;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.nio.file.Paths;
+import java.util.Optional;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.FileLocator;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.Plugin;
+import org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin;
+import org.eclipse.jdt.ls.core.internal.managers.DigestStore;
+import org.eclipse.lsp4j.jsonrpc.Launcher;
+import org.osgi.framework.BundleContext;
+
+import ch.epfl.scala.bsp4j.BuildServer;
+
+public class BuildServerAdapter extends Plugin {
+
+ public static final String PLUGIN_ID = "com.microsoft.buildserver.adapter";
+
+ private static BuildServerAdapter adapterInstance;
+
+ private DigestStore digestStore;
+
+ private BuildServer buildServer;
+ private BspClient buildClient;
+
+ @Override
+ public void start(BundleContext context) throws Exception {
+ BuildServerAdapter.adapterInstance = this;
+ digestStore = new DigestStore(getStateLocation().toFile());
+ }
+
+ @Override
+ public void stop(BundleContext context) throws Exception {
+ BuildServerAdapter.adapterInstance = null;
+ }
+
+ public static BuildServerAdapter getInstance() {
+ return BuildServerAdapter.adapterInstance;
+ }
+
+ public static DigestStore getDigestStore() {
+ return adapterInstance.digestStore;
+ }
+
+ public static BuildServer getBuildServer() {
+ if (adapterInstance.buildServer == null) {
+ String javaExecutablePath = getJavaExecutablePath();
+ if (javaExecutablePath == null || javaExecutablePath.isEmpty()) {
+ JavaLanguageServerPlugin.logError("Failed to get Java executable path.");
+ return null;
+ }
+
+ String[] classpaths = getBuildServerRuntimeClasspath();
+ if (classpaths.length == 0) {
+ JavaLanguageServerPlugin.logError("Failed to get required runtime classpaths for build server.");
+ return null;
+ }
+ String storagePath = ResourcesPlugin.getWorkspace().getRoot().getLocation()
+ .removeLastSegments(2).append("build-server").toFile().getAbsolutePath();
+
+ ProcessBuilder build = new ProcessBuilder(
+ javaExecutablePath,
+ // "-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=8989",
+ "--add-opens=java.base/java.lang=ALL-UNNAMED",
+ "--add-opens=java.base/java.io=ALL-UNNAMED",
+ "--add-opens=java.base/java.util=ALL-UNNAMED",
+ "-DbuildServerStorage=" + storagePath,
+ "-cp",
+ String.join(getClasspathSplitor(), classpaths),
+ "com.microsoft.java.bs.core.JavaBspLauncher"
+ );
+
+ try {
+ Process process = build.start();
+ ExecutorService fixedThreadPool = Executors.newCachedThreadPool();
+ adapterInstance.buildClient = new BspClient();
+ Launcher launcher = new Launcher.Builder()
+ .setOutput(process.getOutputStream())
+ .setInput(process.getInputStream())
+ .setLocalService(adapterInstance.buildClient)
+ .setExecutorService(fixedThreadPool)
+ .setRemoteInterface(BuildServer.class)
+ .create();
+
+ launcher.startListening();
+ adapterInstance.buildServer = launcher.getRemoteProxy();
+ adapterInstance.buildClient.onConnectWithServer(adapterInstance.buildServer);
+ } catch (IOException e) {
+ JavaLanguageServerPlugin.logException("Failed to start build server", e);
+ return null;
+ }
+ }
+ return adapterInstance.buildServer;
+ }
+
+ private static String getJavaExecutablePath() {
+ Optional command = ProcessHandle.current().info().command();
+ if (command.isPresent()) {
+ return command.get();
+ }
+
+ return "";
+ }
+
+ private static String[] getBuildServerRuntimeClasspath() {
+ try {
+ URL fileURL = FileLocator.toFileURL(BuildServerAdapter.class.getResource("/bsp"));
+ File file = new File(fileURL.getPath());
+ return new String[]{
+ Paths.get(file.getAbsolutePath(), "server.jar").toString(),
+ Paths.get(file.getAbsolutePath(), "libs").toString() + Path.SEPARATOR + "*"
+ };
+ } catch (Exception e) {
+ JavaLanguageServerPlugin.logException("Unable to get build server runtime classpath", e);
+ return new String[0];
+ }
+ }
+
+ private static String getClasspathSplitor() {
+ String os = System.getProperty("os.name").toLowerCase();
+
+ if (os.contains("win")) {
+ return ";";
+ }
+
+ return ":"; // Linux or Mac
+ }
+}
diff --git a/jdtls.ext/com.microsoft.buildserver.adapter/src/com/microsoft/buildserver/adapter/BuildServerPreferences.java b/jdtls.ext/com.microsoft.buildserver.adapter/src/com/microsoft/buildserver/adapter/BuildServerPreferences.java
new file mode 100644
index 00000000..ab4bbf8a
--- /dev/null
+++ b/jdtls.ext/com.microsoft.buildserver.adapter/src/com/microsoft/buildserver/adapter/BuildServerPreferences.java
@@ -0,0 +1,78 @@
+package com.microsoft.buildserver.adapter;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * The data used in 'build/initialize' request.
+ */
+public class BuildServerPreferences {
+ private String gradleJavaHome;
+ private String gradleVersion;
+ private String gradleHome;
+ private String gradleUserHome;
+ private boolean gradleWrapperEnabled;
+ private List gradleArguments;
+ private List gradleJvmArguments;
+
+ public BuildServerPreferences() {
+ gradleArguments = Collections.emptyList();
+ gradleJvmArguments = Collections.emptyList();
+ }
+
+ public String getGradleJavaHome() {
+ return gradleJavaHome;
+ }
+
+ public void setGradleJavaHome(String gradleJavaHome) {
+ this.gradleJavaHome = gradleJavaHome;
+ }
+
+ public String getGradleVersion() {
+ return gradleVersion;
+ }
+
+ public void setGradleVersion(String gradleVersion) {
+ this.gradleVersion = gradleVersion;
+ }
+
+ public String getGradleHome() {
+ return gradleHome;
+ }
+
+ public void setGradleHome(String gradleHome) {
+ this.gradleHome = gradleHome;
+ }
+
+ public String getGradleUserHome() {
+ return gradleUserHome;
+ }
+
+ public void setGradleUserHome(String gradleUserHome) {
+ this.gradleUserHome = gradleUserHome;
+ }
+
+ public boolean isGradleWrapperEnabled() {
+ return gradleWrapperEnabled;
+ }
+
+ public void setGradleWrapperEnabled(boolean gradleWrapperEnabled) {
+ this.gradleWrapperEnabled = gradleWrapperEnabled;
+ }
+
+ public List getGradleArguments() {
+ return gradleArguments;
+ }
+
+ public void setGradleArguments(List gradleArguments) {
+ this.gradleArguments = gradleArguments;
+ }
+
+ public List getGradleJvmArguments() {
+ return gradleJvmArguments;
+ }
+
+ public void setGradleJvmArguments(List gradleJvmArguments) {
+ this.gradleJvmArguments = gradleJvmArguments;
+ }
+}
diff --git a/jdtls.ext/com.microsoft.buildserver.adapter/src/com/microsoft/buildserver/adapter/BuildServerTargetsManager.java b/jdtls.ext/com.microsoft.buildserver.adapter/src/com/microsoft/buildserver/adapter/BuildServerTargetsManager.java
new file mode 100644
index 00000000..ee9e1f4c
--- /dev/null
+++ b/jdtls.ext/com.microsoft.buildserver.adapter/src/com/microsoft/buildserver/adapter/BuildServerTargetsManager.java
@@ -0,0 +1,47 @@
+package com.microsoft.buildserver.adapter;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.core.resources.IProject;
+
+import ch.epfl.scala.bsp4j.BuildTarget;
+import ch.epfl.scala.bsp4j.InitializeBuildResult;
+
+public class BuildServerTargetsManager {
+ private BuildServerTargetsManager() {
+ }
+
+ private static class BuildServerTargetsManagerHolder {
+ private static final BuildServerTargetsManager INSTANCE = new BuildServerTargetsManager();
+ }
+
+ public static BuildServerTargetsManager getInstance() {
+ return BuildServerTargetsManagerHolder.INSTANCE;
+ }
+
+ private Map> cache = new HashMap<>();
+ private Map initializeBuildResultCache = new HashMap<>();
+
+ public void reset() {
+ cache.clear();
+ }
+
+ public List getBuildTargets(IProject project) {
+ return cache.get(project);
+ }
+
+ public void setBuildTargets(IProject project, List targets) {
+ cache.put(project, targets);
+ }
+
+ public InitializeBuildResult getInitializeBuildResult(File path) {
+ return initializeBuildResultCache.get(path);
+ }
+
+ public void setInitializeBuildResult(File path, InitializeBuildResult result) {
+ initializeBuildResultCache.put(path, result);
+ }
+}
diff --git a/jdtls.ext/com.microsoft.buildserver.adapter/src/com/microsoft/buildserver/adapter/Constant.java b/jdtls.ext/com.microsoft.buildserver.adapter/src/com/microsoft/buildserver/adapter/Constant.java
new file mode 100644
index 00000000..bb64753f
--- /dev/null
+++ b/jdtls.ext/com.microsoft.buildserver.adapter/src/com/microsoft/buildserver/adapter/Constant.java
@@ -0,0 +1,9 @@
+package com.microsoft.buildserver.adapter;
+
+public final class Constant {
+ private Constant() {}
+
+ public static final String CLIENT_NAME = "jdtls";
+ public static final String CLIENT_VERSION = "1.0.0";
+ public static final String BSP_VERSION = "2.1.0-M4";
+}
diff --git a/jdtls.ext/com.microsoft.buildserver.adapter/src/com/microsoft/buildserver/adapter/bsp4j/extended/JvmBuildTargetExt.java b/jdtls.ext/com.microsoft.buildserver.adapter/src/com/microsoft/buildserver/adapter/bsp4j/extended/JvmBuildTargetExt.java
new file mode 100644
index 00000000..a7b67941
--- /dev/null
+++ b/jdtls.ext/com.microsoft.buildserver.adapter/src/com/microsoft/buildserver/adapter/bsp4j/extended/JvmBuildTargetExt.java
@@ -0,0 +1,61 @@
+package com.microsoft.buildserver.adapter.bsp4j.extended;
+
+import ch.epfl.scala.bsp4j.JvmBuildTarget;
+
+public class JvmBuildTargetExt extends JvmBuildTarget {
+
+ String sourceLanguageLevel;
+
+ String targetBytecodeVersion;
+
+ public JvmBuildTargetExt(String javaHome, String javaVersion) {
+ super(javaHome, javaVersion);
+ }
+
+ public String getSourceLanguageLevel() {
+ return sourceLanguageLevel;
+ }
+
+ public void setSourceLanguageLevel(String sourceLanguageLevel) {
+ this.sourceLanguageLevel = sourceLanguageLevel;
+ }
+
+ public String getTargetBytecodeVersion() {
+ return targetBytecodeVersion;
+ }
+
+ public void setTargetBytecodeVersion(String targetBytecodeVersion) {
+ this.targetBytecodeVersion = targetBytecodeVersion;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = super.hashCode();
+ result = prime * result + ((sourceLanguageLevel == null) ? 0 : sourceLanguageLevel.hashCode());
+ result = prime * result + ((targetBytecodeVersion == null) ? 0 : targetBytecodeVersion.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (!super.equals(obj))
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ JvmBuildTargetExt other = (JvmBuildTargetExt) obj;
+ if (sourceLanguageLevel == null) {
+ if (other.sourceLanguageLevel != null)
+ return false;
+ } else if (!sourceLanguageLevel.equals(other.sourceLanguageLevel))
+ return false;
+ if (targetBytecodeVersion == null) {
+ if (other.targetBytecodeVersion != null)
+ return false;
+ } else if (!targetBytecodeVersion.equals(other.targetBytecodeVersion))
+ return false;
+ return true;
+ }
+}
diff --git a/jdtls.ext/com.microsoft.buildserver.adapter/src/com/microsoft/buildserver/adapter/builder/BspBuilder.java b/jdtls.ext/com.microsoft.buildserver.adapter/src/com/microsoft/buildserver/adapter/builder/BspBuilder.java
new file mode 100644
index 00000000..526fd5d9
--- /dev/null
+++ b/jdtls.ext/com.microsoft.buildserver.adapter/src/com/microsoft/buildserver/adapter/builder/BspBuilder.java
@@ -0,0 +1,65 @@
+package com.microsoft.buildserver.adapter.builder;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IncrementalProjectBuilder;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+
+import com.microsoft.buildserver.adapter.BuildServerAdapter;
+import com.microsoft.buildserver.adapter.BuildServerTargetsManager;
+
+import ch.epfl.scala.bsp4j.BuildServer;
+import ch.epfl.scala.bsp4j.BuildTarget;
+import ch.epfl.scala.bsp4j.BuildTargetIdentifier;
+import ch.epfl.scala.bsp4j.CleanCacheParams;
+import ch.epfl.scala.bsp4j.CompileParams;
+import ch.epfl.scala.bsp4j.CompileResult;
+import ch.epfl.scala.bsp4j.StatusCode;
+
+/**
+ * BspBuilder
+ */
+public class BspBuilder extends IncrementalProjectBuilder {
+
+ public static final String BUILDER_ID = "com.microsoft.buildserver.adapter.builder.bspBuilder";
+
+ @Override
+ protected IProject[] build(int kind, Map args, IProgressMonitor monitor) throws CoreException {
+ // TODO: how to avoid build from the root project when the root project does not contain java files.
+ // building root project will cause all sub-modules being built.
+ BuildServer buildServer = BuildServerAdapter.getBuildServer();
+ if (buildServer != null) {
+ List targets = BuildServerTargetsManager.getInstance().getBuildTargets(this.getProject());
+ List ids = targets.stream().map(BuildTarget::getId).collect(Collectors.toList());
+ if (ids != null) {
+ if (requiresClean(kind)) {
+ buildServer.buildTargetCleanCache(new CleanCacheParams(ids)).join();
+ }
+
+ if (requiresBuild(kind)) {
+ CompileResult result = buildServer.buildTargetCompile(new CompileParams(ids)).join();
+ if (Objects.equals(result.getStatusCode(), StatusCode.ERROR)) {
+ throw new CoreException(new Status(IStatus.ERROR, "testtest", "Build Failed."));
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ private boolean requiresClean(int kind) {
+ // TODO: Currently clean is not supported.
+ return false;
+ }
+
+ private boolean requiresBuild(int kind) {
+ return kind == FULL_BUILD || kind == INCREMENTAL_BUILD;
+ }
+}
diff --git a/jdtls.ext/com.microsoft.jdtls.ext.target/com.microsoft.jdtls.ext.tp.target b/jdtls.ext/com.microsoft.jdtls.ext.target/com.microsoft.jdtls.ext.tp.target
index ad9d88b6..db61f7c9 100644
--- a/jdtls.ext/com.microsoft.jdtls.ext.target/com.microsoft.jdtls.ext.tp.target
+++ b/jdtls.ext/com.microsoft.jdtls.ext.target/com.microsoft.jdtls.ext.tp.target
@@ -25,8 +25,12 @@
-
+
+
+
+
+
diff --git a/jdtls.ext/pom.xml b/jdtls.ext/pom.xml
index 09874c64..4cfa8270 100644
--- a/jdtls.ext/pom.xml
+++ b/jdtls.ext/pom.xml
@@ -24,6 +24,7 @@
com.microsoft.jdtls.ext.core
com.microsoft.jdtls.ext.target
+ com.microsoft.buildserver.adapter
@@ -107,6 +108,27 @@
+
+ org.apache.maven.plugins
+ maven-dependency-plugin
+
+
+
+ get-libs
+
+ copy
+
+ validate
+
+
+
+ false
+ ${basedir}/lib/
+
+ true
+
+
diff --git a/language-support/gradle-output/GradleOutput.tmLanguage.json b/language-support/gradle-output/GradleOutput.tmLanguage.json
new file mode 100644
index 00000000..e872d71f
--- /dev/null
+++ b/language-support/gradle-output/GradleOutput.tmLanguage.json
@@ -0,0 +1,35 @@
+{
+ "name": "Gradle Output",
+ "scopeName": "source.gradle-output",
+ "fileTypes": ["gradle-output"],
+ "patterns": [
+ {
+ "name": "FAILURE",
+ "match": "(^FAILURE: .+$)",
+ "captures": {
+ "1": {"name": "invalid.illegal.failure"}
+ }
+ },
+ {
+ "name": "error",
+ "match": "(^\\d+\\serror$)",
+ "captures": {
+ "1": {"name": "invalid.illegal.error"}
+ }
+ },
+ {
+ "name": "error.description",
+ "match": "(error:\\s.+$)",
+ "captures": {
+ "1": {"name": "invalid.illegal.error.description"}
+ }
+ },
+ {
+ "name": "BUILD FAILED",
+ "match": "(^BUILD FAILED)",
+ "captures": {
+ "1": {"name": "invalid.illegal.bold.buildFailed"}
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/package.json b/package.json
index 1502dbdc..090b95a0 100644
--- a/package.json
+++ b/package.json
@@ -46,7 +46,8 @@
"main": "./main.js",
"contributes": {
"javaExtensions": [
- "./server/com.microsoft.jdtls.ext.core-0.23.0.jar"
+ "./server/com.microsoft.jdtls.ext.core-0.23.0.jar",
+ "./server/com.microsoft.buildserver.adapter-0.23.0.jar"
],
"commands": [
{
@@ -223,6 +224,11 @@
"command": "java.view.package.renameFile",
"title": "%contributes.commands.java.view.package.renameFile%",
"category": "Java"
+ },
+ {
+ "command": "java.buildServer.openLogs",
+ "title": "Open Build Server Log",
+ "category": "Java"
}
],
"configuration": {
@@ -280,6 +286,11 @@
"type": "boolean",
"description": "%configuration.java.project.explorer.showNonJavaResources%",
"default": true
+ },
+ "java.buildServer.gradle.enabled": {
+ "type": "boolean",
+ "description": "[Experimental] Use build server to synchronize Gradle project when enabled.",
+ "default": false
}
}
},
@@ -862,6 +873,18 @@
}
}
}
+ ],
+ "languages": [
+ {
+ "id": "gradle-output"
+ }
+ ],
+ "grammars": [
+ {
+ "language": "gradle-output",
+ "scopeName": "source.gradle-output",
+ "path": "./language-support/gradle-output/GradleOutput.tmLanguage.json"
+ }
]
},
"scripts": {
@@ -870,6 +893,7 @@
"test": "tsc -p . && node ./dist/test/index.js",
"test-ui": "tsc -p . && node ./dist/test/ui/index.js",
"build-server": "node scripts/buildJdtlsExt.js",
+ "build-bsp": "node scripts/buildBuildServer.js",
"vscode:prepublish": "tsc -p ./ && webpack --mode production",
"tslint": "tslint -t verbose --project tsconfig.json"
},
diff --git a/scripts/buildBuildServer.js b/scripts/buildBuildServer.js
new file mode 100644
index 00000000..21e72ef9
--- /dev/null
+++ b/scripts/buildBuildServer.js
@@ -0,0 +1,40 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license.
+
+const cp = require('child_process');
+const fse = require('fs-extra');
+const path = require('path');
+
+const server_dir = path.resolve('build-server-for-java');
+if (!fse.existsSync(server_dir)) {
+ cp.execSync('git clone https://github.com/microsoft/build-server-for-java.git', {stdio:[0,1,2]} );
+}
+
+cp.execSync(gradlew() + ' build -x test', {cwd:server_dir, stdio:[0,1,2]} );
+cp.execSync(gradlew() + ' copyRuntimeLibs', {cwd:server_dir, stdio:[0,1,2]} );
+
+copy(path.join(server_dir, 'server/build/libs'), path.resolve('jdtls.ext/com.microsoft.buildserver.adapter/bsp'), (file) => {
+ return file.endsWith('.jar');
+});
+
+copy(path.join(server_dir, 'server/build/runtime-libs'), path.resolve('jdtls.ext/com.microsoft.buildserver.adapter/bsp/libs'), (file) => {
+ return file.endsWith('.jar');
+});
+
+fse.removeSync(server_dir);
+
+function copy(sourceFolder, targetFolder, fileFilter) {
+ const jars = fse.readdirSync(sourceFolder).filter(file => fileFilter(file));
+ fse.ensureDirSync(targetFolder);
+ for (const jar of jars) {
+ fse.copyFileSync(path.join(sourceFolder, jar), path.join(targetFolder, path.basename(jar)));
+ }
+}
+
+function isWin() {
+ return /^win/.test(process.platform);
+}
+
+function gradlew() {
+ return isWin()?"gradlew.bat":"./gradlew";
+}
\ No newline at end of file
diff --git a/scripts/buildJdtlsExt.js b/scripts/buildJdtlsExt.js
index c6623dbf..57d9f25e 100644
--- a/scripts/buildJdtlsExt.js
+++ b/scripts/buildJdtlsExt.js
@@ -11,6 +11,9 @@ cp.execSync(mvnw()+ ' clean package', {cwd:server_dir, stdio:[0,1,2]} );
copy(path.join(server_dir, 'com.microsoft.jdtls.ext.core/target'), path.resolve('server'), (file) => {
return /^com.microsoft.jdtls.ext.core.*.jar$/.test(file);
});
+copy(path.join(server_dir, 'com.microsoft.buildserver.adapter/target'), path.resolve('server'), (file) => {
+ return /^com.microsoft.buildserver.adapter.*.jar$/.test(file);
+});
function copy(sourceFolder, targetFolder, fileFilter) {
const jars = fse.readdirSync(sourceFolder).filter(file => fileFilter(file));
diff --git a/src/bsp/GradleOutputLinkProvider.ts b/src/bsp/GradleOutputLinkProvider.ts
new file mode 100644
index 00000000..756485d6
--- /dev/null
+++ b/src/bsp/GradleOutputLinkProvider.ts
@@ -0,0 +1,26 @@
+import {DocumentLink, DocumentLinkProvider, ProviderResult, Range, TextDocument, Uri } from "vscode";
+
+export class GradleOutputLinkProvider implements DocumentLinkProvider {
+ provideDocumentLinks(document: TextDocument): ProviderResult {
+ const links: DocumentLink[] = [];
+ const content = document.getText();
+ let searchPosition = 0;
+ const lines = content.split(/\r?\n/g);
+ for (const line of lines) {
+ const match = line.match(/(.*\.java):(\d+)/);
+ if (match) {
+ const startOffset = content.indexOf(match[0], searchPosition);
+ const start = document.positionAt(startOffset);
+ const endOffset = startOffset + match[0].length;
+ const end = document.positionAt(endOffset);
+ searchPosition += endOffset;
+
+ const file = match[1];
+ const line = parseInt(match[2]);
+ const uri = Uri.file(file).with({ fragment: `L${line}` });
+ links.push(new DocumentLink(new Range(start, end), uri));
+ }
+ }
+ return links;
+ }
+}
diff --git a/src/bsp/bspController.ts b/src/bsp/bspController.ts
new file mode 100644
index 00000000..8e01d606
--- /dev/null
+++ b/src/bsp/bspController.ts
@@ -0,0 +1,53 @@
+import { ConfigurationTarget, Disposable, ExtensionContext, OutputChannel, Uri, commands, languages, window, workspace } from "vscode";
+import { GradleOutputLinkProvider } from "./GradleOutputLinkProvider";
+import * as path from "path";
+import * as fse from "fs-extra";
+
+export class BspController implements Disposable {
+
+ private disposable: Disposable;
+ private bsOutputChannel: OutputChannel;
+
+ public constructor(public readonly context: ExtensionContext) {
+ this.bsOutputChannel = window.createOutputChannel("Build Output", "gradle-output");
+ this.disposable = Disposable.from(
+ commands.registerCommand("java.buildServer.openLogs", async () => {
+ const storagePath: string | undefined = context.storageUri?.fsPath;
+ if (storagePath) {
+ const logFile = path.join(storagePath, "..", "build-server", "bs.log");
+ if (await fse.pathExists(logFile)) {
+ await window.showTextDocument(Uri.file(logFile));
+ } else {
+ window.showErrorMessage("Failed to find build server log file.");
+ }
+ }
+ }),
+ commands.registerCommand("_java.buildServer.gradle.buildStart", (msg: string) => {
+ this.bsOutputChannel.appendLine(`> Build started at ${ new Date().toLocaleString()} <\n`);
+ this.bsOutputChannel.appendLine(msg);
+ }),
+ commands.registerCommand("_java.buildServer.gradle.buildProgress", (msg: string) => {
+ this.bsOutputChannel.appendLine(msg);
+ }),
+ commands.registerCommand("_java.buildServer.gradle.buildComplete", (msg: string) => {
+ if (msg) {
+ this.bsOutputChannel.appendLine(`\n${msg}`);
+ this.bsOutputChannel.appendLine('------\n');
+ if (msg.includes("BUILD FAILED in")) {
+ this.bsOutputChannel.show(true);
+ }
+ }
+ }),
+ commands.registerCommand("_java.buildServer.configAutoBuild", async () => {
+ await workspace.getConfiguration("java").update("autobuild.enabled", false, ConfigurationTarget.Workspace);
+ window.showInformationMessage("Auto build is turned off to get the best experience. You can reset it later in Settings.");
+ }),
+ this.bsOutputChannel,
+ languages.registerDocumentLinkProvider({ language: "gradle-output", scheme: 'output' }, new GradleOutputLinkProvider()),
+ );
+ }
+
+ public dispose() {
+ this.disposable.dispose();
+ }
+}
diff --git a/src/extension.ts b/src/extension.ts
index 4f4ad709..93d2fc3d 100644
--- a/src/extension.ts
+++ b/src/extension.ts
@@ -20,6 +20,7 @@ import { DiagnosticProvider } from "./tasks/buildArtifact/migration/DiagnosticPr
import { setContextForDeprecatedTasks, updateExportTaskType } from "./tasks/buildArtifact/migration/utils";
import { CodeActionProvider } from "./tasks/buildArtifact/migration/CodeActionProvider";
import { newJavaClass } from "./explorerCommands/new";
+import { BspController } from "./bsp/bspController";
export async function activate(context: ExtensionContext): Promise {
contextManager.initialize(context);
@@ -41,6 +42,7 @@ async function activateExtension(_operationId: string, context: ExtensionContext
context.subscriptions.push(new ProjectController(context));
Settings.initialize(context);
context.subscriptions.push(new LibraryController(context));
+ context.subscriptions.push(new BspController(context));
context.subscriptions.push(DependencyExplorer.getInstance(context));
context.subscriptions.push(contextManager);
context.subscriptions.push(syncHandler);