Skip to content

Commit

Permalink
Add info to hover for feature
Browse files Browse the repository at this point in the history
  • Loading branch information
cherylking committed Jan 22, 2024
1 parent d2319e9 commit ab4ffb2
Show file tree
Hide file tree
Showing 5 changed files with 152 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,24 @@
import org.eclipse.lsp4j.MarkupContent;
import org.eclipse.lsp4j.jsonrpc.CancelChecker;

import io.openliberty.tools.langserver.lemminx.data.FeatureListGraph;
import io.openliberty.tools.langserver.lemminx.data.FeatureListNode;
import io.openliberty.tools.langserver.lemminx.data.LibertyRuntime;
import io.openliberty.tools.langserver.lemminx.models.feature.*;
import io.openliberty.tools.langserver.lemminx.services.FeatureService;
import io.openliberty.tools.langserver.lemminx.services.LibertyProjectsManager;
import io.openliberty.tools.langserver.lemminx.services.LibertyWorkspace;
import io.openliberty.tools.langserver.lemminx.services.SettingsService;
import io.openliberty.tools.langserver.lemminx.util.*;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Optional;
import java.util.Set;
import java.util.logging.Logger;

public class LibertyHoverParticipant implements IHoverParticipant {
private static final Logger LOGGER = Logger.getLogger(LibertyHoverParticipant.class.getName());

@Override
public Hover onAttributeName(IHoverRequest request, CancelChecker cancelChecker) {
Expand Down Expand Up @@ -63,7 +72,7 @@ public Hover onText(IHoverRequest request, CancelChecker cancelChecker) {
return null;
}

private Hover getHoverFeatureDescription(String featureName, DOMDocument domDocument) {
private Hover getFeatureDescription(String featureName, DOMDocument domDocument) {
LibertyRuntime runtimeInfo = LibertyUtils.getLibertyRuntimeInfo(domDocument);
String libertyVersion = runtimeInfo == null ? null : runtimeInfo.getRuntimeVersion();
String libertyRuntime = runtimeInfo == null ? null : runtimeInfo.getRuntimeType();
Expand All @@ -76,4 +85,59 @@ private Hover getHoverFeatureDescription(String featureName, DOMDocument domDocu

return null;
}

private Hover getHoverFeatureDescription(String featureName, DOMDocument document) {
LibertyWorkspace ws = LibertyProjectsManager.getInstance().getWorkspaceFolder(document.getDocumentURI());
FeatureListGraph featureGraph = null;
if (ws == null) {
LOGGER.warning("Could not get workspace for: "+document.getDocumentURI() + ". Using cached feature list for hover.");
featureGraph = FeatureService.getInstance().getDefaultFeatureList();
} else {
featureGraph = ws.getFeatureListGraph();
}

FeatureListNode flNode = featureGraph.get(featureName);
if (flNode == null) {
LOGGER.warning("Could not get full description for feature: "+featureName+" from cached feature list.");
return getFeatureDescription(featureName, document);
}

StringBuilder sb = new StringBuilder();
String description = flNode.getDescription();
sb.append("Description: ");
sb.append(description);
sb.append(System.lineSeparator());

// getAllEnabledBy would return all transitive features but typically offers too much
Set<String> featureEnabledBy = flNode.getEnabledBy();
if (!featureEnabledBy.isEmpty()) {
sb.append("Enabled by: ");
// Need to sort the collection of features so that they are in a reliable order for tests.
ArrayList<String> sortedFeatures = new ArrayList<String>();
sortedFeatures.addAll(featureEnabledBy);
Collections.sort(sortedFeatures);
for (String nextFeature : sortedFeatures) {
sb.append(nextFeature);
sb.append(", ");
}
sb.setLength(sb.length() - 2);
sb.append(System.lineSeparator());
}

Set<String> featureEnables = flNode.getEnablesFeatures();
if (!featureEnables.isEmpty()) {
sb.append("Enables: ");
// Need to sort the collection of features so that they are in a reliable order for tests.
ArrayList<String> sortedFeatures = new ArrayList<String>();
sortedFeatures.addAll(featureEnables);
Collections.sort(sortedFeatures);
for (String nextFeature : sortedFeatures) {
sb.append(nextFeature);
sb.append(", ");
}
sb.setLength(sb.length() - 2);
}

return new Hover(new MarkupContent("plaintext", sb.toString()));
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2023 IBM Corporation and others.
* Copyright (c) 2023, 2024 IBM Corporation and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
Expand All @@ -22,11 +22,13 @@
public class FeatureListGraph {
private String runtime = "";
private Map<String, FeatureListNode> nodes;
private Map<String, Set<String>> enabledByCache; // storing in lower case to enable diagnostics with configured features
private Map<String, Set<String>> enabledByCache;
private Map<String, Set<String>> enabledByCacheLowerCase; // storing in lower case to enable diagnostics with configured features
private Map<String, Set<String>> enablesCache;

public FeatureListGraph() {
nodes = new HashMap<String, FeatureListNode>();
enabledByCacheLowerCase = new HashMap<String, Set<String>>();
enabledByCache = new HashMap<String, Set<String>>();
enablesCache = new HashMap<String, Set<String>>();
}
Expand All @@ -40,6 +42,19 @@ public FeatureListNode addFeature(String nodeName) {
return node;
}

public FeatureListNode addFeature(String nodeName, String description) {
if (nodes.containsKey(nodeName)) {
FeatureListNode node = nodes.get(nodeName);
if (node.getDescription().isEmpty()) {
node.setDescription(description);
}
return node;
}
FeatureListNode node = new FeatureListNode(nodeName, description);
nodes.put(nodeName, node);
return node;
}

public FeatureListNode addConfigElement(String nodeName) {
if (nodes.containsKey(nodeName)) {
return nodes.get(nodeName);
Expand Down Expand Up @@ -79,9 +94,26 @@ public String getRuntime() {
* @return
*/
public Set<String> getAllEnabledBy(String elementName) {
return getAllEnabledBy(elementName, true);
}

/**
* Returns a superset of 'owning' features that enable a given config element or feature.
* The features are returned in lower case if the 'lowerCase' boolean is true. Otherwise,
* the features are returned in their original case.
* @param elementName
* @return
*/
public Set<String> getAllEnabledBy(String elementName, boolean lowerCase) {

if (lowerCase && enabledByCacheLowerCase.containsKey(elementName)) {
return enabledByCacheLowerCase.get(elementName);
}

if (enabledByCache.containsKey(elementName)) {
return enabledByCache.get(elementName);
}

if (!nodes.containsKey(elementName)) {
return null;
}
Expand All @@ -100,16 +132,22 @@ public Set<String> getAllEnabledBy(String elementName) {
allEnabledBy.addAll(enablers);
queue.addAll(enablers);
}
return addToEnabledByCacheInLowerCase(elementName, allEnabledBy);
return addToEnabledByCache(elementName, allEnabledBy, lowerCase);
}

private Set<String> addToEnabledByCacheInLowerCase(String configElement, Set<String> allEnabledBy) {
private Set<String> addToEnabledByCache(String configElement, Set<String> allEnabledBy, boolean lowerCase) {
Set<String> lowercaseEnabledBy = new HashSet<String>();
Set<String> originalcaseEnabledBy = new HashSet<String>();
originalcaseEnabledBy.addAll(allEnabledBy);

for (String nextFeature: allEnabledBy) {
lowercaseEnabledBy.add(nextFeature.toLowerCase());
}
enabledByCache.put(configElement, lowercaseEnabledBy);
return lowercaseEnabledBy;

enabledByCacheLowerCase.put(configElement, lowercaseEnabledBy);
enabledByCache.put(configElement, originalcaseEnabledBy);

return lowerCase ? lowercaseEnabledBy : originalcaseEnabledBy;
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2023 IBM Corporation and others.
* Copyright (c) 2023, 2024 IBM Corporation and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
Expand All @@ -19,6 +19,7 @@
// Class to represent a feature OR config element in a feature list xml
public class FeatureListNode {
protected String nodeName;
protected String description; // only used for features
protected Set<String> enabledBy;
protected Set<String> enables;

Expand All @@ -28,6 +29,21 @@ public FeatureListNode(String nodeName) {
this.nodeName = nodeName;
}

public FeatureListNode(String nodeName, String description) {
enabledBy = new HashSet<String>();
enables = new HashSet<String>();
this.nodeName = nodeName;
this.description = description;
}

public void setDescription(String description) {
this.description = description;
}

public String getDescription() {
return this.description == null ? "" : this.description;
}

public void addEnabledBy(String nodeName) {
enabledBy.add(nodeName);
}
Expand All @@ -44,6 +60,16 @@ public Set<String> getEnables() {
return enables;
}

public Set<String> getEnablesFeatures() {
Set<String> enablesFeatures = new HashSet<String>();
for (String next: enables) {
if (next.contains("-")) {
enablesFeatures.add(next);
}
}
return enablesFeatures;
}

// based on a heuristic that features use major versions and config elements don't use '.'
public boolean isConfigElement() {
return this.nodeName.indexOf('.') == -1;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2020, 2023 IBM Corporation and others.
* Copyright (c) 2020, 2024 IBM Corporation and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
Expand Down Expand Up @@ -494,7 +494,7 @@ public List<Feature> readFeaturesFromFeatureListFile(List<Feature> installedFeat
String currentFeature = f.getName();
List<String> enables = f.getEnables();
List<String> configElements = f.getConfigElements();
FeatureListNode currentFeatureNode = featureListGraph.addFeature(currentFeature);
FeatureListNode currentFeatureNode = featureListGraph.addFeature(currentFeature, f.getDescription());
if (enables != null) {
for (String enabledFeature : enables) {
FeatureListNode feature = featureListGraph.addFeature(enabledFeature);
Expand Down
20 changes: 14 additions & 6 deletions lemminx-liberty/src/test/java/io/openliberty/LibertyHoverTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,14 @@ public void testFeatureHover() throws BadLocationException {
);

XMLAssert.assertHover(serverXML, serverXMLURI,
"This feature enables support for Java API for RESTful Web Services v2.1. "
+ "JAX-RS annotations can be used to define web service clients and endpoints that comply with the REST architectural style. "
+ "Endpoints are accessed through a common interface that is based on the HTTP standard methods.",
r(2, 24, 2, 33));
"Description: This feature enables support for Java API for RESTful Web Services v2.1. "
+ "JAX-RS annotations can be used to define web service clients and endpoints that comply with the REST architectural style. "
+ "Endpoints are accessed through a common interface that is based on the HTTP standard methods."
+ System.lineSeparator()
+ "Enabled by: microProfile-2.0, microProfile-2.1, microProfile-2.2, microProfile-3.0, microProfile-3.2, microProfile-3.3, microProfile-4.0, microProfile-4.1, mpOpenAPI-2.0, opentracing-1.3, opentracing-2.0, webProfile-8.0"
+ System.lineSeparator()
+ "Enables: jaxrsClient-2.1, servlet-4.0",
r(2, 24, 2, 33));

}

Expand All @@ -45,9 +49,13 @@ public void testFeatureHoverTrim() throws BadLocationException {
);

XMLAssert.assertHover(serverXML, serverXMLURI,
"This feature enables support for Java API for RESTful Web Services v2.1. "
"Description: This feature enables support for Java API for RESTful Web Services v2.1. "
+ "JAX-RS annotations can be used to define web service clients and endpoints that comply with the REST architectural style. "
+ "Endpoints are accessed through a common interface that is based on the HTTP standard methods.",
+ "Endpoints are accessed through a common interface that is based on the HTTP standard methods."
+ System.lineSeparator()
+ "Enabled by: microProfile-2.0, microProfile-2.1, microProfile-2.2, microProfile-3.0, microProfile-3.2, microProfile-3.3, microProfile-4.0, microProfile-4.1, mpOpenAPI-2.0, opentracing-1.3, opentracing-2.0, webProfile-8.0"
+ System.lineSeparator()
+ "Enables: jaxrsClient-2.1, servlet-4.0",
r(2, 24, 2, 34));

}
Expand Down

0 comments on commit ab4ffb2

Please sign in to comment.