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

Reply via email to