diff --git a/nbcode/telemetry/src/org/netbeans/modules/nbcode/java/lsp/server/telemetry/SourceInfo.java b/nbcode/telemetry/src/org/netbeans/modules/nbcode/java/lsp/server/telemetry/SourceInfo.java index 9c6adac3..101eb2be 100644 --- a/nbcode/telemetry/src/org/netbeans/modules/nbcode/java/lsp/server/telemetry/SourceInfo.java +++ b/nbcode/telemetry/src/org/netbeans/modules/nbcode/java/lsp/server/telemetry/SourceInfo.java @@ -20,7 +20,10 @@ import java.lang.ref.WeakReference; import java.net.URI; import java.util.Map; +import java.util.concurrent.ExecutionException; import java.util.function.Function; +import java.util.logging.Level; +import java.util.logging.Logger; import javax.tools.JavaFileObject; import javax.tools.SimpleJavaFileObject; import org.eclipse.lsp4j.services.LanguageClient; @@ -38,7 +41,7 @@ import org.openide.util.Lookup; class SourceInfo { - + private static final Logger LOG = Logger.getLogger(SourceInfo.class.getName()); private final FileObject file; private final Project owner; final JavaFileObject source; @@ -108,10 +111,15 @@ public String getJavaVersion() { return LspServerTelemetryManager.getJavaRuntimeVersion(lookupFunction); } - public boolean getPreviewEnabled() { - return LspServerTelemetryManager.getInstance().isPreviewEnabled(file, - owner == null ? LspServerTelemetryManager.ProjectType.standalone : LspServerTelemetryManager.getInstance().getProjectType(owner), - getLanguageClient()); + public boolean getPreviewEnabled(){ + try { + return LspServerTelemetryManager.getInstance().isPreviewEnabled(file, + owner, + getLanguageClient()).get(); + } catch (InterruptedException | ExecutionException ex) { + LOG.log(Level.FINE, "exception while checking if preview enabled: {0}", (Object) ex); + } + return false; } public static SourceInfo getSourceObject(ErrorProvider.Context context) { diff --git a/patches/nb-telemetry.diff b/patches/nb-telemetry.diff index 358845e1..be9accc0 100644 --- a/patches/nb-telemetry.diff +++ b/patches/nb-telemetry.diff @@ -1,5 +1,5 @@ diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/LspServerTelemetryManager.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/LspServerTelemetryManager.java -index d82646afb1..b008279cc4 100644 +index d82646afb1..a4a1b53e26 100644 --- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/LspServerTelemetryManager.java +++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/LspServerTelemetryManager.java @@ -21,6 +21,7 @@ package org.netbeans.modules.java.lsp.server.protocol; @@ -10,7 +10,7 @@ index d82646afb1..b008279cc4 100644 import java.math.BigInteger; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; -@@ -28,25 +29,29 @@ import java.security.NoSuchAlgorithmException; +@@ -28,25 +29,36 @@ import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -18,10 +18,11 @@ index d82646afb1..b008279cc4 100644 +import java.util.Iterator; import java.util.List; import java.util.Map; --import java.util.Set; +import java.util.NavigableMap; + import java.util.Set; +import java.util.TreeMap; import java.util.WeakHashMap; ++import java.util.concurrent.CompletableFuture; import java.util.concurrent.Future; -import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Function; @@ -32,19 +33,24 @@ index d82646afb1..b008279cc4 100644 import org.eclipse.lsp4j.ConfigurationParams; import org.eclipse.lsp4j.MessageType; import org.eclipse.lsp4j.services.LanguageClient; ++import org.netbeans.api.annotations.common.NonNull; +import org.netbeans.api.java.platform.JavaPlatform; ++import org.netbeans.api.java.project.JavaProjectConstants; import org.netbeans.api.java.queries.CompilerOptionsQuery; import org.netbeans.api.java.queries.CompilerOptionsQuery.Result; import org.netbeans.api.project.Project; import org.netbeans.api.project.ProjectManager; ++import org.netbeans.api.project.ProjectUtils; ++import org.netbeans.api.project.SourceGroup; import org.netbeans.api.project.ui.ProjectProblems; +import org.netbeans.modules.java.platform.implspi.JavaPlatformProvider; import org.openide.filesystems.FileObject; -import org.openide.util.Exceptions; ++import org.openide.filesystems.FileUtil; import org.openide.util.Lookup; /** -@@ -55,130 +60,200 @@ import org.openide.util.Lookup; +@@ -55,130 +67,261 @@ import org.openide.util.Lookup; */ public class LspServerTelemetryManager { @@ -89,11 +95,11 @@ index d82646afb1..b008279cc4 100644 + private final WeakHashMap>> clients = new WeakHashMap<>(); + private volatile boolean telemetryEnabled = false; + private long lspServerIntializationTime; - ++ + public boolean isTelemetryEnabled() { + return telemetryEnabled; + } -+ + + public void connect(LanguageClient client, Future future) { synchronized (clients) { - for (Map.Entry> entry : clients.entrySet()) { @@ -167,20 +173,21 @@ index d82646afb1..b008279cc4 100644 } - public void sendWorkspaceInfo(LanguageClient client, List workspaceClientFolders, Collection prjs, long timeToOpenPrjs) { +- JsonObject properties = new JsonObject(); +- JsonArray prjProps = new JsonArray(); + private boolean isInvalidClient(WeakReference> closeListener) { + Future close = closeListener == null ? null : closeListener.get(); + return close == null || close.isDone(); + } -+ -+ public void sendWorkspaceInfo(LanguageClient client, List workspaceClientFolders, Collection projects, long timeToOpenProjects) { - JsonObject properties = new JsonObject(); - JsonArray prjProps = new JsonArray(); - Map mp = prjs.stream() - .collect(Collectors.toMap(project -> project.getProjectDirectory().getPath(), project -> project)); ++ public CompletableFuture sendWorkspaceInfo(LanguageClient client, List workspaceClientFolders, Collection projects, long timeToOpenProjects) { ++ JsonObject properties = new JsonObject(); + + NavigableMap mp = projects.stream() + .collect(Collectors.toMap(project -> project.getProjectDirectory().getPath(), project -> project, (p1, p2) -> p1, TreeMap::new)); - ++ List> createProjectFutures = new ArrayList<>(); for (FileObject workspaceFolder : workspaceClientFolders) { try { - JsonObject obj = new JsonObject(); @@ -188,81 +195,77 @@ index d82646afb1..b008279cc4 100644 String prjPath = workspaceFolder.getPath(); - String prjId = this.getPrjId(prjPath); - obj.addProperty("id", prjId); -- -- // In future if different JDK is used for different project then this can be updated -- obj.addProperty("javaVersion", System.getProperty("java.version")); -- -- if (mp.containsKey(prjPath)) { -- Project prj = mp.get(prjPath); -- -- ProjectManager.Result r = ProjectManager.getDefault().isProject2(prj.getProjectDirectory()); -- String projectType = r.getProjectType(); -- obj.addProperty("buildTool", (projectType.contains("maven") ? "MavenProject" : "GradleProject")); -- -- obj.addProperty("openedWithProblems", ProjectProblems.isBroken(prj)); -- -- boolean isPreviewFlagEnabled = this.isEnablePreivew(prj.getProjectDirectory(), projectType); -- obj.addProperty("enablePreview", isPreviewFlagEnabled); -- } else { -- obj.addProperty("buildTool", this.STANDALONE_PRJ); -- obj.addProperty("javaVersion", System.getProperty("java.version")); -- obj.addProperty("openedWithProblems", false); -- -- boolean isPreviewFlagEnabled = this.isEnablePreivew(workspaceFolder, this.STANDALONE_PRJ); -- obj.addProperty("enablePreview", isPreviewFlagEnabled); + String prjPathWithSlash = null; + for (Map.Entry p : mp.tailMap(prjPath, true).entrySet()) { + String projectPath = p.getKey(); + if (prjPathWithSlash == null) { + if (prjPath.equals(projectPath)) { -+ prjProps.add(createProjectInfo(prjPath, p.getValue(), workspaceFolder, client)); ++ createProjectFutures.add(createProjectInfo(prjPath, p.getValue(), workspaceFolder, client)); + noProjectFound = false; + break; + } -+ prjPathWithSlash = prjPath + '/'; ++ prjPathWithSlash = prjPath + '/'; + } + if (projectPath.startsWith(prjPathWithSlash)) { -+ prjProps.add(createProjectInfo(p.getKey(), p.getValue(), workspaceFolder, client)); ++ createProjectFutures.add(createProjectInfo(p.getKey(), p.getValue(), workspaceFolder, client)); + noProjectFound = false; + continue; + } + break; - } -- -- prjProps.add(obj); -- -- } catch (NoSuchAlgorithmException ex) { -- Exceptions.printStackTrace(ex); ++ } + if (noProjectFound) { + // No project found -+ prjProps.add(createProjectInfo(prjPath, null, workspaceFolder, client)); ++ createProjectFutures.add(createProjectInfo(prjPath, null, workspaceFolder, client)); + } + } catch (NoSuchAlgorithmException e) { + LOG.log(Level.INFO, "NoSuchAlgorithmException while creating workspaceInfo event: {0}", e.getMessage()); + } catch (Exception e) { + LOG.log(Level.INFO, "Exception while creating workspaceInfo event: {0}", e.getMessage()); - } - } ++ } ++ } + +- // In future if different JDK is used for different project then this can be updated +- obj.addProperty("javaVersion", System.getProperty("java.version")); ++ return CompletableFuture.allOf(createProjectFutures.toArray(new CompletableFuture[0])) ++ .thenApply((ignored) -> { ++ JsonArray prjProps = new JsonArray(); ++ createProjectFutures.forEach((f) -> prjProps.add(f.join())); ++ return prjProps; ++ }) ++ .thenAccept((prjProps) -> { ++ properties.add("projectInfo", prjProps); -- properties.add("prjsInfo", prjProps); -+ properties.add("projectInfo", prjProps); +- if (mp.containsKey(prjPath)) { +- Project prj = mp.get(prjPath); ++ properties.addProperty("projInitTimeTaken", timeToOpenProjects); ++ properties.addProperty("numProjects", workspaceClientFolders.size()); ++ properties.addProperty("lspInitTimeTaken", System.currentTimeMillis() - this.lspServerIntializationTime); -- properties.addProperty("timeToOpenPrjs", timeToOpenPrjs); -- properties.addProperty("numOfPrjsOpened", workspaceClientFolders.size()); -- properties.addProperty("lspServerInitializationTime", System.currentTimeMillis() - this.lspServerIntiailizationTime); -+ properties.addProperty("projInitTimeTaken", timeToOpenProjects); -+ properties.addProperty("numProjects", workspaceClientFolders.size()); -+ properties.addProperty("lspInitTimeTaken", System.currentTimeMillis() - this.lspServerIntializationTime); +- ProjectManager.Result r = ProjectManager.getDefault().isProject2(prj.getProjectDirectory()); +- String projectType = r.getProjectType(); +- obj.addProperty("buildTool", (projectType.contains("maven") ? "MavenProject" : "GradleProject")); +- +- obj.addProperty("openedWithProblems", ProjectProblems.isBroken(prj)); +- +- boolean isPreviewFlagEnabled = this.isEnablePreivew(prj.getProjectDirectory(), projectType); +- obj.addProperty("enablePreview", isPreviewFlagEnabled); +- } else { +- obj.addProperty("buildTool", this.STANDALONE_PRJ); +- obj.addProperty("javaVersion", System.getProperty("java.version")); +- obj.addProperty("openedWithProblems", false); +- +- boolean isPreviewFlagEnabled = this.isEnablePreivew(workspaceFolder, this.STANDALONE_PRJ); +- obj.addProperty("enablePreview", isPreviewFlagEnabled); +- } +- +- prjProps.add(obj); ++ this.sendTelemetry(client, new TelemetryEvent(MessageType.Info.toString(), LspServerTelemetryManager.WORKSPACE_INFO_EVT, properties)); ++ }); ++ } -- this.sendTelemetry(client, new TelemetryEvent(MessageType.Info.toString(), this.WORKSPACE_INFO_EVT, properties)); -+ this.sendTelemetry(client, new TelemetryEvent(MessageType.Info.toString(), LspServerTelemetryManager.WORKSPACE_INFO_EVT, properties)); - } -- -- private boolean isEnablePreivew(FileObject source, String prjType) { -- if (prjType.equals(this.STANDALONE_PRJ)) { -- NbCodeLanguageClient client = Lookup.getDefault().lookup(NbCodeLanguageClient.class); -+ -+ private JsonObject createProjectInfo(String prjPath, Project prj, FileObject workspaceFolder, LanguageClient client) throws NoSuchAlgorithmException { +- } catch (NoSuchAlgorithmException ex) { +- Exceptions.printStackTrace(ex); ++ private CompletableFuture createProjectInfo(String prjPath, Project prj, FileObject workspaceFolder, LanguageClient client) throws NoSuchAlgorithmException { + JsonObject obj = new JsonObject(); + String prjId = getPrjId(prjPath); + obj.addProperty("id", prjId); @@ -280,58 +283,120 @@ index d82646afb1..b008279cc4 100644 + } catch (RuntimeException e) { + LOG.log(Level.INFO, "Exception while checking project problems for workspaceInfo event: {0}", e.getMessage()); + projectHasProblems = true; -+ } + } + obj.addProperty("isOpenedWithProblems", projectHasProblems); -+ } + } + String javaVersion = getProjectJavaVersion(); + obj.addProperty("javaVersion", javaVersion); -+ obj.addProperty("buildTool", projectType.name()); -+ boolean isPreviewFlagEnabled = isPreviewEnabled(projectDirectory, projectType, client); -+ obj.addProperty("isPreviewEnabled", isPreviewFlagEnabled); -+ return obj; -+ } -+ -+ public boolean isPreviewEnabled(FileObject source, ProjectType prjType) { -+ return isPreviewEnabled(source, prjType, null); ++ if (projectType != null) obj.addProperty("buildTool", projectType.name()); ++ return isPreviewEnabled(projectDirectory, prj, client).thenApply(isPreviewFlagEnabled -> { ++ obj.addProperty("isPreviewEnabled", isPreviewFlagEnabled); ++ return obj; ++ }); + } + +- properties.add("prjsInfo", prjProps); +- +- properties.addProperty("timeToOpenPrjs", timeToOpenPrjs); +- properties.addProperty("numOfPrjsOpened", workspaceClientFolders.size()); +- properties.addProperty("lspServerInitializationTime", System.currentTimeMillis() - this.lspServerIntiailizationTime); +- +- this.sendTelemetry(client, new TelemetryEvent(MessageType.Info.toString(), this.WORKSPACE_INFO_EVT, properties)); ++ public CompletableFuture isPreviewEnabled(FileObject source, Project prj) { ++ return isPreviewEnabled(source, prj, null); + } +- +- private boolean isEnablePreivew(FileObject source, String prjType) { +- if (prjType.equals(this.STANDALONE_PRJ)) { +- NbCodeLanguageClient client = Lookup.getDefault().lookup(NbCodeLanguageClient.class); + -+ public boolean isPreviewEnabled(FileObject source, ProjectType prjType, LanguageClient languageClient) { ++ public CompletableFuture isPreviewEnabled(FileObject source, Project prj, LanguageClient languageClient) { ++ ProjectType prjType = prj == null ? ProjectType.standalone : getProjectType(prj); + if (prjType == ProjectType.standalone) { -+ NbCodeLanguageClient client = languageClient instanceof NbCodeLanguageClient ? (NbCodeLanguageClient) languageClient : null ; ++ NbCodeLanguageClient client = languageClient instanceof NbCodeLanguageClient ? (NbCodeLanguageClient) languageClient : null; if (client == null) { - return false; + client = Lookup.getDefault().lookup(NbCodeLanguageClient.class); + if (client == null) { -+ return false; ++ return CompletableFuture.completedFuture(false); + } } - AtomicBoolean isEnablePreviewSet = new AtomicBoolean(false); -+ boolean[] isEnablePreviewSet = {false}; ConfigurationItem conf = new ConfigurationItem(); - conf.setSection(client.getNbCodeCapabilities().getAltConfigurationPrefix() + "runConfig.vmOptions"); - client.configuration(new ConfigurationParams(Collections.singletonList(conf))).thenAccept(c -> { - String config = ((JsonPrimitive) ((List) c).get(0)).getAsString(); - isEnablePreviewSet.set(config.contains(this.ENABLE_PREVIEW)); -- }); -- -- return isEnablePreviewSet.get(); + conf.setSection(client.getNbCodeCapabilities().getConfigurationPrefix() + "runConfig.vmOptions"); -+ client.configuration(new ConfigurationParams(Collections.singletonList(conf))) -+ .thenAccept(c -> { -+ isEnablePreviewSet[0] = c != null && !c.isEmpty() ++ return client.configuration(new ConfigurationParams(Collections.singletonList(conf))) ++ .thenApply(c -> { ++ return c != null && !c.isEmpty() + && ((JsonPrimitive) c.get(0)).getAsString().contains(ENABLE_PREVIEW); -+ }); -+ return isEnablePreviewSet[0]; + }); + +- return isEnablePreviewSet.get(); } -- + ++ boolean previewEnabled; ++ previewEnabled = source != null && isPreviewEnabledForSource(source); ++ if (!previewEnabled) { ++ assert prj != null; ++ FileObject prjRoot = prj.getProjectDirectory(); ++ String relativePath = prjRoot == null ? null : FileUtil.getRelativePath(prjRoot, source); ++ if (relativePath == null || relativePath.isEmpty()) { ++ // The source is not inside the project root, and, ++ // so the project contents need to be checked for preview-enabled ++ previewEnabled = isPreviewEnabledForAnyProjectSourceRoot(prj); ++ previewEnabled = previewEnabled || isPreviewEnabledForAnyContainedProjects(prj); ++ } ++ } ++ ++ return CompletableFuture.completedFuture(previewEnabled); ++ } ++ ++ private boolean isPreviewEnabledForAnyContainedProjects(@NonNull Project project) { ++ Set subProjects = ProjectUtils.getContainedProjects(project, false); ++ if (subProjects != null) { ++ for (Project subProject : subProjects) { ++ if (isPreviewEnabledForAnyProjectSourceRoot(subProject)) { ++ return true; ++ } ++ } ++ for (Project subProject : subProjects) { ++ if (isPreviewEnabledForAnyContainedProjects(subProject)) { ++ return true; ++ } ++ } ++ } ++ return false; ++ } ++ ++ private boolean isPreviewEnabledForAnyProjectSourceRoot(@NonNull Project project) { ++ SourceGroup[] sources = ProjectUtils.getSources(project).getSourceGroups(JavaProjectConstants.SOURCES_TYPE_JAVA); ++ if (sources == null || sources.length == 0) { ++ FileObject root = project.getProjectDirectory(); ++ if (root != null && isPreviewEnabledForSource(root)) { ++ return true; ++ } ++ } else { ++ for (SourceGroup s : sources) { ++ FileObject root = s.getRootFolder(); ++ if (root != null && isPreviewEnabledForSource(root)) { ++ return true; ++ } ++ } ++ } ++ return false; ++ } + ++ private boolean isPreviewEnabledForSource(@NonNull FileObject source) { Result result = CompilerOptionsQuery.getOptions(source); - return result.getArguments().contains(this.ENABLE_PREVIEW); + return result.getArguments().contains(ENABLE_PREVIEW); } private String getPrjId(String prjPath) throws NoSuchAlgorithmException { -@@ -187,15 +262,50 @@ public class LspServerTelemetryManager { +@@ -187,15 +330,54 @@ public class LspServerTelemetryManager { BigInteger number = new BigInteger(1, hash); @@ -380,9 +445,13 @@ index d82646afb1..b008279cc4 100644 + } + + public ProjectType getProjectType(Project prj) { -+ ProjectManager.Result r = ProjectManager.getDefault().isProject2(prj.getProjectDirectory()); ++ FileObject prjDir = prj.getProjectDirectory(); ++ ProjectManager.Result r = prjDir == null ? null : ProjectManager.getDefault().isProject2(prjDir); + String projectType = r == null ? null : r.getProjectType(); -+ return projectType != null && projectType.contains(ProjectType.maven.name()) ? ProjectType.maven : ProjectType.gradle; ++ return projectType == null ? null ++ : projectType.contains(ProjectType.maven.name()) ? ProjectType.maven ++ : projectType.contains(ProjectType.gradle.name()) ? ProjectType.gradle ++ : null; + } } diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/NbCodeClientCapabilities.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/NbCodeClientCapabilities.java