This is an automated email from the ASF dual-hosted git repository.
sdedic pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/netbeans.git
The following commit(s) were added to refs/heads/master by this push:
new c0eb652d22 Project info exported through LSP.
new 099a506504 Merge pull request #4595 from sdedic/lsp/projectInfo
c0eb652d22 is described below
commit c0eb652d2231c09b70c8ada39887b170a2c49dc1
Author: Svata Dedic <[email protected]>
AuthorDate: Wed Sep 7 11:29:48 2022 +0200
Project info exported through LSP.
---
.../java/lsp/server/project/LspProjectInfo.java | 62 +++++++
.../modules/java/lsp/server/protocol/Server.java | 10 +-
.../lsp/server/protocol/WorkspaceServiceImpl.java | 185 +++++++++++++++++++++
3 files changed, 256 insertions(+), 1 deletion(-)
diff --git
a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/project/LspProjectInfo.java
b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/project/LspProjectInfo.java
new file mode 100644
index 0000000000..4e267b68f7
--- /dev/null
+++
b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/project/LspProjectInfo.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.java.lsp.server.project;
+
+import java.net.URI;
+
+/**
+ *
+ * @author sdedic
+ */
+public class LspProjectInfo {
+ /**
+ * Project's directory
+ */
+ public URI projectDirectory;
+
+ /**
+ * Project's name.
+ */
+ public String name;
+
+ /**
+ * Project's display name as defined in project file(s)
+ */
+ public String displayName;
+
+ /**
+ * The build system / project type. Ant, Gradle, Maven.
+ */
+ public String projectType;
+
+ /**
+ * URIs of subprojects. Usually children of the project's own directory.
+ */
+ public URI[] subprojects;
+
+ /**
+ * If part of a reactor or multi-project, the URI of the root project.
+ */
+ public URI rootProject;
+
+ /**
+ * Supported project actions. Names.
+ */
+ public String[] projectActionNames;
+}
diff --git
a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/Server.java
b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/Server.java
index cdcf5cf636..df866c4ccd 100644
---
a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/Server.java
+++
b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/Server.java
@@ -749,7 +749,9 @@ public final class Server {
JAVA_SUPER_IMPLEMENTATION,
JAVA_SOURCE_FOR,
JAVA_CLEAR_PROJECT_CACHES,
- NATIVE_IMAGE_FIND_DEBUG_PROCESS_TO_ATTACH));
+ NATIVE_IMAGE_FIND_DEBUG_PROCESS_TO_ATTACH,
+ JAVA_PROJECT_INFO
+ ));
for (CodeActionsProvider codeActionsProvider :
Lookup.getDefault().lookupAll(CodeActionsProvider.class)) {
commands.addAll(codeActionsProvider.getCommands());
}
@@ -944,6 +946,12 @@ public final class Server {
* new project files were generated into workspace subtree.
*/
public static final String JAVA_CLEAR_PROJECT_CACHES =
"java.clear.project.caches";
+
+ /**
+ * For a project directory, returns basic project information and
structure.
+ * Syntax: nbls.project.info(locations : String | String[], options? : {
projectStructure? : boolean; actions? : boolean; recursive? : boolean }) :
LspProjectInfo
+ */
+ public static final String JAVA_PROJECT_INFO = "nbls.project.info";
static final String INDEXING_COMPLETED = "Indexing completed.";
static final String NO_JAVA_SUPPORT = "Cannot initialize Java support on
JDK ";
diff --git
a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/WorkspaceServiceImpl.java
b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/WorkspaceServiceImpl.java
index 352906751a..2060a4546a 100644
---
a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/WorkspaceServiceImpl.java
+++
b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/WorkspaceServiceImpl.java
@@ -19,6 +19,7 @@
package org.netbeans.modules.java.lsp.server.protocol;
import com.google.gson.Gson;
+import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
@@ -29,6 +30,7 @@ import java.io.IOException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URI;
+import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLDecoder;
import java.net.URLEncoder;
@@ -48,6 +50,7 @@ import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
+import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
@@ -99,6 +102,7 @@ import org.netbeans.api.java.source.SourceUtils;
import org.netbeans.api.java.source.ui.ElementOpen;
import org.netbeans.api.project.FileOwnerQuery;
import org.netbeans.api.project.Project;
+import org.netbeans.api.project.ProjectInformation;
import org.netbeans.api.project.ProjectManager;
import org.netbeans.api.project.ProjectUtils;
import org.netbeans.api.project.SourceGroup;
@@ -111,6 +115,7 @@ import org.netbeans.modules.java.lsp.server.LspServerState;
import org.netbeans.modules.java.lsp.server.Utils;
import
org.netbeans.modules.java.lsp.server.debugging.attach.AttachConfigurations;
import
org.netbeans.modules.java.lsp.server.debugging.attach.AttachNativeConfigurations;
+import org.netbeans.modules.java.lsp.server.project.LspProjectInfo;
import org.netbeans.modules.java.source.ElementHandleAccessor;
import org.netbeans.modules.java.source.ui.JavaSymbolProvider;
import org.netbeans.modules.java.source.ui.JavaTypeProvider;
@@ -139,8 +144,11 @@ import org.openide.util.lookup.Lookups;
* @author lahvac
*/
public final class WorkspaceServiceImpl implements WorkspaceService,
LanguageClientAware {
+
+ private static final Logger LOG =
Logger.getLogger(WorkspaceServiceImpl.class.getName());
private static final RequestProcessor WORKER = new
RequestProcessor(WorkspaceServiceImpl.class.getName(), 1, false, false);
+ private static final RequestProcessor PROJECT_WORKER = new
RequestProcessor(WorkspaceServiceImpl.class.getName(), 5, false, false);
private final Gson gson = new Gson();
private final LspServerState server;
@@ -578,6 +586,56 @@ public final class WorkspaceServiceImpl implements
WorkspaceService, LanguageCli
}
return (CompletableFuture<Object>)
(CompletableFuture<?>)result;
}
+
+ case Server.JAVA_PROJECT_INFO: {
+ final CompletableFuture<Object> result = new
CompletableFuture<>();
+ List<Object> arguments = params.getArguments();
+ if (arguments.size() < 1) {
+ result.completeExceptionally(new
IllegalArgumentException("Expecting URL or URL[] as an argument to " +
command));
+ return result;
+ }
+ Object o = arguments.get(0);
+ URL[] locations = null;
+ if (o instanceof JsonArray) {
+ List<URL> locs = new ArrayList<>();
+ JsonArray a = (JsonArray)o;
+ a.forEach((e) -> {
+ if (e instanceof JsonPrimitive) {
+ String s = ((JsonPrimitive)e).getAsString();
+ try {
+ locs.add(new URL(s));
+ } catch (MalformedURLException ex) {
+ throw new IllegalArgumentException("Illegal
location: " + s);
+ }
+ }
+ });
+ } else if (o instanceof JsonPrimitive) {
+ String s = ((JsonPrimitive)o).getAsString();
+ try {
+ locations = new URL[] { new URL(s) };
+ } catch (MalformedURLException ex) {
+ throw new IllegalArgumentException("Illegal location:
" + s);
+ }
+ }
+ if (locations == null || locations.length == 0) {
+ result.completeExceptionally(new
IllegalArgumentException("Expecting URL or URL[] as an argument to " +
command));
+ return result;
+ }
+ boolean projectStructure = false;
+ boolean actions = false;
+ boolean recursive = false;
+
+ if (arguments.size() > 1) {
+ Object a2 = arguments.get(1);
+ if (a2 instanceof JsonObject) {
+ JsonObject options = (JsonObject)a2;
+ projectStructure = getOption(options,
"projectStructure", false); // NOI18N
+ actions = getOption(options, "actions", false); //
NOI18N
+ recursive = getOption(options, "recursive", false); //
NOI18N
+ }
+ }
+ return (CompletableFuture<Object>)(CompletableFuture<?>)new
ProjectInfoWorker(locations, projectStructure, recursive, actions).process();
+ }
default:
for (CodeActionsProvider codeActionsProvider :
Lookup.getDefault().lookupAll(CodeActionsProvider.class)) {
if (codeActionsProvider.getCommands().contains(command)) {
@@ -588,6 +646,133 @@ public final class WorkspaceServiceImpl implements
WorkspaceService, LanguageCli
throw new UnsupportedOperationException("Command not supported: " +
params.getCommand());
}
+ private class ProjectInfoWorker {
+ final URL[] locations;
+ final boolean projectStructure;
+ final boolean recursive;
+ final boolean actions;
+
+ Map<FileObject, LspProjectInfo> infos = new HashMap<>();
+ Set<Project> toOpen = new HashSet<>();
+
+ public ProjectInfoWorker(URL[] locations, boolean projectStructure,
boolean recursive, boolean actions) {
+ this.locations = locations;
+ this.projectStructure = projectStructure;
+ this.recursive = recursive;
+ this.actions = actions;
+ }
+
+ public CompletableFuture<LspProjectInfo[]> process() {
+ List<FileObject> files = new ArrayList();
+ for (URL u : locations) {
+ FileObject f = URLMapper.findFileObject(u);
+ if (f != null) {
+ files.add(f);
+ }
+ }
+ return server.asyncOpenSelectedProjects(files,
false).thenCompose(this::processProjects);
+ }
+
+ LspProjectInfo fillProjectInfo(Project p) {
+ LspProjectInfo info = infos.get(p.getProjectDirectory());
+ if (info != null) {
+ return info;
+ }
+ info = new LspProjectInfo();
+
+ ProjectInformation pi = ProjectUtils.getInformation(p);
+ URL projectURL = URLMapper.findURL(p.getProjectDirectory(),
URLMapper.EXTERNAL);
+ if (projectURL != null) {
+ try {
+ info.projectDirectory = projectURL.toURI();
+ } catch (URISyntaxException ex) {
+ // should not happen
+ }
+ }
+ info.name = pi.getName();
+ info.displayName = pi.getDisplayName();
+
+ // attempt to determine the project type
+ ProjectManager.Result r =
ProjectManager.getDefault().isProject2(p.getProjectDirectory());
+ info.projectType = r.getProjectType();
+
+ if (actions) {
+ ActionProvider ap = p.getLookup().lookup(ActionProvider.class);
+ if (ap != null) {
+ info.projectActionNames = ap.getSupportedActions();
+ }
+ }
+
+ if (projectStructure) {
+ Set<Project> children = ProjectUtils.getContainedProjects(p,
false);
+ List<URI> subprojectDirs = new ArrayList<>();
+ for (Project c : children) {
+ try {
+
subprojectDirs.add(URLMapper.findURL(c.getProjectDirectory(),
URLMapper.EXTERNAL).toURI());
+ } catch (URISyntaxException ex) {
+ // should not happen
+ }
+ }
+ info.subprojects = subprojectDirs.toArray(new
URI[subprojectDirs.size()]);
+ Project root = ProjectUtils.rootOf(p);
+ if (root != null) {
+ try {
+ info.rootProject =
URLMapper.findURL(root.getProjectDirectory(), URLMapper.EXTERNAL).toURI();
+ } catch (URISyntaxException ex) {
+ // should not happen
+ }
+ }
+ if (recursive) {
+ toOpen.addAll(children);
+ }
+ }
+ infos.put(p.getProjectDirectory(), info);
+ return info;
+ }
+
+ CompletableFuture<LspProjectInfo[]> processProjects(Project[] prjs) {
+ for (Project p : prjs) {
+ fillProjectInfo(p);
+ }
+ if (toOpen.isEmpty()) {
+ return finalizeInfos();
+ }
+ List<FileObject> dirs = new ArrayList<>(toOpen.size());
+ for (Project p : toOpen) {
+ dirs.add(p.getProjectDirectory());
+ }
+ toOpen.clear();
+ return
server.asyncOpenSelectedProjects(dirs).thenCompose(this::processProjects);
+ }
+
+ CompletableFuture<LspProjectInfo[]> finalizeInfos() {
+ List<LspProjectInfo> list = new ArrayList();
+ for (URL u : locations) {
+ FileObject f = URLMapper.findFileObject(u);
+ Project owner = FileOwnerQuery.getOwner(f);
+ if (owner != null) {
+ list.add(infos.remove(owner.getProjectDirectory()));
+ } else {
+ list.add(null);
+ }
+ }
+ list.addAll(infos.values());
+ LspProjectInfo[] toArray = list.toArray(new
LspProjectInfo[list.size()]);
+ return CompletableFuture.completedFuture(toArray);
+ }
+ }
+
+ private static boolean getOption(JsonObject opts, String member, boolean
def) {
+ if (!opts.has(member)) {
+ return def;
+ }
+ Object o = opts.get(member);
+ if (!(o instanceof JsonPrimitive)) {
+ return false;
+ }
+ return ((JsonPrimitive)o).getAsBoolean();
+ }
+
private final AtomicReference<BiConsumer<FileObject,
Collection<TestMethodController.TestMethod>>> testMethodsListener = new
AtomicReference<>();
private final AtomicReference<PropertyChangeListener> openProjectsListener
= new AtomicReference<>();
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]
For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists