diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index ca8531bf..91f0d72e 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -32,11 +32,19 @@ jobs:
os: [ubuntu-latest, windows-latest]
steps:
- uses: actions/checkout@v3
+ - name: Checkout ci.common
+ uses: actions/checkout@v3
+ with:
+ repository: OpenLiberty/ci.common
+ path: ci.common
- name: Set up Java
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: 17
+ - name: Build ci common
+ working-directory: ci.common
+ run: ./mvnw -V clean install --batch-mode --no-transfer-progress --errors -DtrimStackTrace=false -DskipTests
- name: Build Lemminx Liberty
working-directory: ./lemminx-liberty
run: ./mvnw clean package -ntp -DskipTests
diff --git a/lemminx-liberty/pom.xml b/lemminx-liberty/pom.xml
index dc3af9b5..c5ae7226 100644
--- a/lemminx-liberty/pom.xml
+++ b/lemminx-liberty/pom.xml
@@ -155,6 +155,29 @@
jakarta.xml.bind-api
4.0.0
+
+ org.mockito
+ mockito-core
+ 5.11.0
+ test
+
+
+ org.mockito
+ mockito-junit-jupiter
+ 5.12.0
+ test
+
+
+ io.openliberty.tools
+ ci.common
+ 1.8.36-SNAPSHOT
+
+
+ junit
+ junit
+
+
+
javax.activation
activation
diff --git a/lemminx-liberty/src/it/variable-processing-ol-it/pom.xml b/lemminx-liberty/src/it/variable-processing-ol-it/pom.xml
new file mode 100644
index 00000000..8aea820b
--- /dev/null
+++ b/lemminx-liberty/src/it/variable-processing-ol-it/pom.xml
@@ -0,0 +1,168 @@
+
+
+ 4.0.0
+
+ io.openliberty.tools.test
+ variable-processing-ol-it
+ 1.0-SNAPSHOT
+ pom
+
+
+
+ io.openliberty.tools
+ liberty-langserver-lemminx
+ @pom.version@
+
+
+ org.eclipse.lemminx
+ org.eclipse.lemminx
+ ${lemminx.version}
+ provided
+
+
+ xml-apis
+ xml-apis
+
+
+
+
+ org.eclipse.lemminx
+ org.eclipse.lemminx
+ ${lemminx.version}
+ test
+ tests
+ test-jar
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+ 5.6.1
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ 5.6.1
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter-params
+ 5.6.1
+ test
+
+
+
+
+
+
+
+ io.openliberty.tools
+ liberty-maven-plugin
+ 3.7.1
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.8.1
+
+
+ org.apache.maven.plugins
+ maven-failsafe-plugin
+ 3.0.0-M5
+
+
+
+
+
+ io.openliberty.tools
+ liberty-maven-plugin
+
+ test
+
+ io.openliberty
+ openliberty-runtime
+ 24.0.0.11
+
+
+
+
+ install-server
+ pre-integration-test
+
+ create
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+ 1.8
+
+
+
+ test-compile
+ compile
+
+ testCompile
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-failsafe-plugin
+
+
+ integration-test
+
+ integration-test
+
+
+
+ verify
+ install
+
+ verify
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ 3.0.0-M5
+
+ true
+
+
+
+
+
+
+ lemminx-releases
+ https://repo.eclipse.org/content/repositories/lemminx-releases/
+
+ false
+
+
+ true
+
+
+
+ lemminx-snapshots
+ https://repo.eclipse.org/content/repositories/lemminx-snapshots/
+
+ false
+
+
+ true
+
+
+
+
diff --git a/lemminx-liberty/src/it/variable-processing-ol-it/src/main/liberty/config/bootstrap.properties b/lemminx-liberty/src/it/variable-processing-ol-it/src/main/liberty/config/bootstrap.properties
new file mode 100644
index 00000000..d91d2998
--- /dev/null
+++ b/lemminx-liberty/src/it/variable-processing-ol-it/src/main/liberty/config/bootstrap.properties
@@ -0,0 +1,6 @@
+com.ibm.hpel.trace.purgeMaxSize=0
+default.http.port = 9080
+default.https.port = 9443
+com.ibm.ws.logging.message.format=json
+
+var.this.new=test
\ No newline at end of file
diff --git a/lemminx-liberty/src/it/variable-processing-ol-it/src/main/liberty/config/server.env b/lemminx-liberty/src/it/variable-processing-ol-it/src/main/liberty/config/server.env
new file mode 100644
index 00000000..b6067508
--- /dev/null
+++ b/lemminx-liberty/src/it/variable-processing-ol-it/src/main/liberty/config/server.env
@@ -0,0 +1,4 @@
+WLP_LOGGING_CONSOLE_FORMAT=TBASIC
+TEST_VAR=apple
+NEW_VAR=test2
+NEW_V=true
\ No newline at end of file
diff --git a/lemminx-liberty/src/it/variable-processing-ol-it/src/main/liberty/config/server.xml b/lemminx-liberty/src/it/variable-processing-ol-it/src/main/liberty/config/server.xml
new file mode 100644
index 00000000..c7c02bcc
--- /dev/null
+++ b/lemminx-liberty/src/it/variable-processing-ol-it/src/main/liberty/config/server.xml
@@ -0,0 +1,43 @@
+
+
+
+
+ javaee-6.0
+ acmeCA-2.0
+ servlet-3.1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/lemminx-liberty/src/it/variable-processing-ol-it/src/test/java/LibertyWorkspaceIT.java b/lemminx-liberty/src/it/variable-processing-ol-it/src/test/java/LibertyWorkspaceIT.java
new file mode 100644
index 00000000..b2988a02
--- /dev/null
+++ b/lemminx-liberty/src/it/variable-processing-ol-it/src/test/java/LibertyWorkspaceIT.java
@@ -0,0 +1,187 @@
+package io.openliberty.tools.test;
+
+import java.util.List;
+import java.io.File;
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+
+import org.eclipse.lemminx.XMLAssert;
+import org.eclipse.lemminx.commons.BadLocationException;
+import org.eclipse.lsp4j.CodeAction;
+import org.eclipse.lsp4j.CompletionItem;
+import org.eclipse.lsp4j.Diagnostic;
+import org.eclipse.lsp4j.DiagnosticSeverity;
+import org.eclipse.lsp4j.TextDocumentEdit;
+import org.eclipse.lsp4j.TextEdit;
+import org.eclipse.lsp4j.WorkspaceFolder;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.Test;
+
+import io.openliberty.tools.langserver.lemminx.services.LibertyProjectsManager;
+import io.openliberty.tools.langserver.lemminx.util.LibertyUtils;
+
+import static org.eclipse.lemminx.XMLAssert.*;
+
+public class LibertyWorkspaceIT {
+ static String newLine = System.lineSeparator();
+
+ @AfterAll
+ public static void tearDown() {
+ LibertyProjectsManager.getInstance().cleanInstance();
+ assert(LibertyProjectsManager.getInstance().getLibertyWorkspaceFolders().isEmpty());
+ }
+
+ @Test
+ public void testGetVariables() throws BadLocationException {
+ File testFolder = new File(System.getProperty("user.dir"));
+ File serverXmlFile = new File(testFolder, "src/main/liberty/config/server.xml");
+
+ //Configure Liberty workspace for testing
+ WorkspaceFolder testWorkspace = new WorkspaceFolder(testFolder.toURI().toString());
+ List testWorkspaceFolders = new ArrayList();
+ testWorkspaceFolders.add(testWorkspace);
+ LibertyProjectsManager.getInstance().setWorkspaceFolders(testWorkspaceFolders);
+
+ String serverXML = String.join(newLine, //
+ "", //
+ " ", //
+ " javaee-6.0", //
+ " acmeCA-2.0", //
+ " ", //
+ " ",//
+ "" //
+ );
+
+ CompletionItem httpCompletion = c("${default.http.port}", "${default.http.port}");
+ CompletionItem httpsCompletion = c("${default.https.port}", "${default.https.port}");
+ final int TOTAL_ITEMS = 2; // total number of available completion items containing "default"
+
+ XMLAssert.testCompletionFor(serverXML, null, serverXmlFile.toURI().toString(), TOTAL_ITEMS, httpCompletion,
+ httpsCompletion);
+
+ }
+
+ @Test
+ public void testVariableHover() throws BadLocationException {
+
+ File testFolder = new File(System.getProperty("user.dir"));
+ File serverXmlFile = new File(testFolder, "src/main/liberty/config/server.xml");
+
+ //Configure Liberty workspace for testing
+ WorkspaceFolder testWorkspace = new WorkspaceFolder(testFolder.toURI().toString());
+ List testWorkspaceFolders = new ArrayList();
+ testWorkspaceFolders.add(testWorkspace);
+ LibertyProjectsManager.getInstance().setWorkspaceFolders(testWorkspaceFolders);
+
+ String serverXML = String.join(newLine, //
+ "", //
+ " ", //
+ " javaee-6.0", //
+ " acmeCA-2.0", //
+ " ", //
+ " ",//
+ "" //
+ );
+
+ XMLAssert.assertHover(serverXML, serverXmlFile.toURI().toString(),
+ "default.http.port = 9080",
+ r(5, 33, 5, 55));
+ }
+
+ @Test
+ public void testInvalidVariableDiagnostic() {
+
+ File testFolder = new File(System.getProperty("user.dir"));
+ File serverXmlFile = new File(testFolder, "src/main/liberty/config/server.xml");
+
+ //Configure Liberty workspace for testing
+ WorkspaceFolder testWorkspace = new WorkspaceFolder(testFolder.toURI().toString());
+ List testWorkspaceFolders = new ArrayList();
+ testWorkspaceFolders.add(testWorkspace);
+ LibertyProjectsManager.getInstance().setWorkspaceFolders(testWorkspaceFolders);
+
+ String serverXML = String.join(newLine, //
+ "", //
+ " ", //
+ " javaee-6.0", //
+ " acmeCA-2.0", //
+ " ", //
+ " ",//
+ "" //
+ );
+
+ Diagnostic dup1 = new Diagnostic();
+ dup1.setRange(r(5, 36, 5, 55));
+ dup1.setCode("incorrect_variable");
+ dup1.setSource("liberty-lemminx");
+ dup1.setSeverity(DiagnosticSeverity.Error);
+ dup1.setMessage("ERROR: The variable \"default.httpsl.port\" does not exist.");
+ dup1.setData("default.httpsl.port");
+
+ Diagnostic dup2 = new Diagnostic();
+ dup2.setRange(r(7, 31, 7, 50));
+ dup2.setCode("incorrect_variable");
+ dup2.setSource("liberty-lemminx");
+ dup2.setSeverity(DiagnosticSeverity.Error);
+ dup2.setMessage("ERROR: The variable \"default.httpsj.port\" does not exist.");
+ dup2.setData("default.httpsj.port");
+
+ XMLAssert.testDiagnosticsFor(serverXML, null, null, serverXmlFile.toURI().toString(), false, dup1, dup2);
+ }
+
+ @Test
+ public void testInvalidVariableDiagnosticWithCodeAction() throws IOException, BadLocationException {
+
+ File testFolder = new File(System.getProperty("user.dir"));
+ File serverXmlFile = new File(testFolder, "src/main/liberty/config/server.xml");
+
+ //Configure Liberty workspace for testing
+ WorkspaceFolder testWorkspace = new WorkspaceFolder(testFolder.toURI().toString());
+ List testWorkspaceFolders = new ArrayList();
+ testWorkspaceFolders.add(testWorkspace);
+ LibertyProjectsManager.getInstance().setWorkspaceFolders(testWorkspaceFolders);
+ String serverXML = String.join(newLine, //
+ "", //
+ " ", //
+ " javaee-6.0", //
+ " acmeCA-2.0", //
+ " ", //
+ " ",//
+ "" //
+ );
+
+ Diagnostic invalid1 = new Diagnostic();
+ invalid1.setRange(r(7, 31, 7, 44));
+ invalid1.setCode("incorrect_variable");
+ invalid1.setMessage("ERROR: The variable \"default.https\" does not exist.");
+ invalid1.setData("default.https");
+ invalid1.setSource("liberty-lemminx");
+ invalid1.setSeverity(DiagnosticSeverity.Error);
+
+ XMLAssert.testDiagnosticsFor(serverXML, null, null, serverXmlFile.toURI().toString(), false, invalid1);
+
+ // expecting code action to show only default.https.port
+ // 1. user has entered "default.https"
+ List variables = new ArrayList<>();
+ variables.add("default.https.port");
+ List codeActions = new ArrayList<>();
+ for (String nextVar : variables) {
+ String variableInDoc = String.format("${%s}", nextVar);
+ TextEdit texted = te(invalid1.getRange().getStart().getLine(), invalid1.getRange().getStart().getCharacter(),
+ invalid1.getRange().getEnd().getLine(), invalid1.getRange().getEnd().getCharacter(), variableInDoc);
+ CodeAction invalidCodeAction = ca(invalid1, texted);
+ codeActions.add(invalidCodeAction);
+ invalidCodeAction.getEdit()
+ .getDocumentChanges()
+ .get(0).getLeft().getTextDocument()
+ .setUri(serverXmlFile.toURI().toString());
+ }
+
+ XMLAssert.testCodeActionsFor(serverXML, serverXmlFile.toURI().toString(), invalid1, codeActions.get(0));
+ }
+}
diff --git a/lemminx-liberty/src/main/java/io/openliberty/tools/langserver/lemminx/LibertyCodeActionParticipant.java b/lemminx-liberty/src/main/java/io/openliberty/tools/langserver/lemminx/LibertyCodeActionParticipant.java
index 0b5f8401..8d8b5cd1 100644
--- a/lemminx-liberty/src/main/java/io/openliberty/tools/langserver/lemminx/LibertyCodeActionParticipant.java
+++ b/lemminx-liberty/src/main/java/io/openliberty/tools/langserver/lemminx/LibertyCodeActionParticipant.java
@@ -18,6 +18,7 @@
import java.util.Map;
import io.openliberty.tools.langserver.lemminx.codeactions.ReplacePlatform;
+import io.openliberty.tools.langserver.lemminx.codeactions.ReplaceVariable;
import org.eclipse.lemminx.services.extensions.codeaction.ICodeActionParticipant;
import org.eclipse.lemminx.services.extensions.codeaction.ICodeActionRequest;
import org.eclipse.lsp4j.CodeAction;
@@ -60,6 +61,7 @@ private void registerCodeActions() {
codeActionParticipants.put(LibertyDiagnosticParticipant.IMPLICIT_NOT_OPTIONAL_CODE, new AddAttribute());
codeActionParticipants.put(LibertyDiagnosticParticipant.INCORRECT_FEATURE_CODE, new ReplaceFeature());
codeActionParticipants.put(LibertyDiagnosticParticipant.INCORRECT_PLATFORM_CODE, new ReplacePlatform());
+ codeActionParticipants.put(LibertyDiagnosticParticipant.INCORRECT_VARIABLE_CODE, new ReplaceVariable());
codeActionParticipants.put(LibertyDiagnosticParticipant.MISSING_CONFIGURED_FEATURE_CODE, new AddFeature());
codeActionParticipants.put(LibertyDiagnosticParticipant.IS_FILE_NOT_DIR_CODE, new RemoveTrailingSlash());
codeActionParticipants.put(LibertyDiagnosticParticipant.Is_DIR_NOT_FILE_CODE, new AddTrailingSlash());
diff --git a/lemminx-liberty/src/main/java/io/openliberty/tools/langserver/lemminx/LibertyCompletionParticipant.java b/lemminx-liberty/src/main/java/io/openliberty/tools/langserver/lemminx/LibertyCompletionParticipant.java
index 83c9dc60..c2ccc534 100644
--- a/lemminx-liberty/src/main/java/io/openliberty/tools/langserver/lemminx/LibertyCompletionParticipant.java
+++ b/lemminx-liberty/src/main/java/io/openliberty/tools/langserver/lemminx/LibertyCompletionParticipant.java
@@ -16,6 +16,7 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
+import java.util.Properties;
import java.util.Set;
import java.util.stream.Collectors;
@@ -28,6 +29,7 @@
import org.eclipse.lemminx.services.extensions.completion.ICompletionResponse;
import org.eclipse.lemminx.utils.XMLPositionUtility;
import org.eclipse.lsp4j.CompletionItem;
+import org.eclipse.lsp4j.CompletionItemKind;
import org.eclipse.lsp4j.InsertReplaceEdit;
import org.eclipse.lsp4j.Range;
import org.eclipse.lsp4j.TextEdit;
@@ -43,6 +45,33 @@
public class LibertyCompletionParticipant extends CompletionParticipantAdapter {
+
+ @Override
+ public void onAttributeValue(String valuePrefix, ICompletionRequest request, ICompletionResponse response, CancelChecker cancelChecker) throws Exception {
+ if (!LibertyUtils.isConfigXMLFile(request.getXMLDocument()))
+ return;
+ Properties variableProps = SettingsService.getInstance()
+ .getVariablesForServerXml(request.getXMLDocument()
+ .getDocumentURI());
+ String variableName = valuePrefix.replace("$", "")
+ .replace("{", "")
+ .replace("}", "");
+ variableProps.entrySet().stream().filter(it -> it.getKey().toString().toLowerCase().contains(variableName.toLowerCase()))
+ .forEach(variableProp -> {
+ String varValue = String.format("${%s}", variableProp.getKey());
+
+ Either edit = Either.forLeft(new
+ TextEdit(request.getReplaceRange(), varValue));
+ CompletionItem completionItem = new CompletionItem();
+ completionItem.setLabel(varValue);
+ completionItem.setTextEdit(edit);
+ completionItem.setFilterText(variableProp.getKey().toString());
+ completionItem.setKind(CompletionItemKind.Value);
+ completionItem.setDocumentation(String.format("%s = %s", variableProp.getKey(),variableProp.getValue()));
+ response.addCompletionItem(completionItem);
+ });
+ }
+
@Override
public void onXMLContent(ICompletionRequest request, ICompletionResponse response, CancelChecker cancelChecker)
throws IOException, BadLocationException {
diff --git a/lemminx-liberty/src/main/java/io/openliberty/tools/langserver/lemminx/LibertyDiagnosticParticipant.java b/lemminx-liberty/src/main/java/io/openliberty/tools/langserver/lemminx/LibertyDiagnosticParticipant.java
index 2b05aa37..79887574 100644
--- a/lemminx-liberty/src/main/java/io/openliberty/tools/langserver/lemminx/LibertyDiagnosticParticipant.java
+++ b/lemminx-liberty/src/main/java/io/openliberty/tools/langserver/lemminx/LibertyDiagnosticParticipant.java
@@ -13,6 +13,7 @@
package io.openliberty.tools.langserver.lemminx;
import com.google.common.collect.Sets;
+import io.openliberty.tools.langserver.lemminx.models.feature.VariableLoc;
import org.eclipse.lemminx.dom.DOMAttr;
import org.eclipse.lemminx.dom.DOMDocument;
import org.eclipse.lemminx.dom.DOMNode;
@@ -40,6 +41,7 @@
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.Properties;
import java.util.Set;
import java.util.logging.Logger;
import java.util.stream.Collectors;
@@ -71,6 +73,7 @@ public class LibertyDiagnosticParticipant implements IDiagnosticsParticipant {
public static final String INCORRECT_FEATURE_CODE = "incorrect_feature";
public static final String INCORRECT_PLATFORM_CODE = "incorrect_platform";
+ public static final String INCORRECT_VARIABLE_CODE = "incorrect_variable";
@Override
public void doDiagnostics(DOMDocument domDocument, List diagnostics,
@@ -107,8 +110,38 @@ private void validateDom(DOMDocument domDocument, List diagnosticsLi
}
}
validateConfigElements(domDocument, diagnosticsList, tempDiagnosticsList, featureGraph, includedFeatures, featureManagerPresent);
+ validateVariables(domDocument,diagnosticsList);
}
+ private void validateVariables(DOMDocument domDocument, List diagnosticsList) {
+ String docContent = domDocument.getTextDocument().getText();
+ List variables = LibertyUtils.getVariablesFromTextContent(docContent);
+ Properties variablesMap = SettingsService.getInstance().getVariablesForServerXml(domDocument.getDocumentURI());
+ if (variablesMap.isEmpty() && !variables.isEmpty()) {
+ String message = "WARNING: Variable resolution is not available for workspace %s. Please start the Liberty server for the workspace to enable variable resolution.";
+ LibertyWorkspace workspace = LibertyProjectsManager.getInstance().getWorkspaceFolder(domDocument.getDocumentURI());
+ Range range = XMLPositionUtility.createRange(domDocument.getDocumentElement().getStartTagOpenOffset(), domDocument.getDocumentElement().getStartTagCloseOffset(),
+ domDocument);
+ Diagnostic diag = new Diagnostic(range, message.formatted(workspace.getWorkspaceURI().getPath()), DiagnosticSeverity.Warning, LIBERTY_LEMMINX_SOURCE);
+ diagnosticsList.add(diag);
+ return;
+ }
+ for (VariableLoc variable : variables) {
+ if (!variablesMap.containsKey(variable.getValue())) {
+ String variableInDoc = String.format("${%s}", variable.getValue());
+ Range range = XMLPositionUtility.createRange(variable.getStartLoc(),variable.getEndLoc(),
+ domDocument);
+ String message = "ERROR: The variable \"" + variable.getValue() + "\" does not exist.";
+
+ Diagnostic diag = new Diagnostic(range, message, DiagnosticSeverity.Error, LIBERTY_LEMMINX_SOURCE, INCORRECT_VARIABLE_CODE);
+ diag.setData(variable.getValue());
+ diagnosticsList.add(diag);
+ }
+ }
+ }
+
+
+
private void validateFeaturesAndPlatforms(DOMDocument domDocument, List list, DOMNode featureManager, Set includedFeatures) {
LibertyRuntime runtimeInfo = LibertyUtils.getLibertyRuntimeInfo(domDocument);
diff --git a/lemminx-liberty/src/main/java/io/openliberty/tools/langserver/lemminx/LibertyExtension.java b/lemminx-liberty/src/main/java/io/openliberty/tools/langserver/lemminx/LibertyExtension.java
index 509c4577..dc65d5e4 100644
--- a/lemminx-liberty/src/main/java/io/openliberty/tools/langserver/lemminx/LibertyExtension.java
+++ b/lemminx-liberty/src/main/java/io/openliberty/tools/langserver/lemminx/LibertyExtension.java
@@ -12,6 +12,7 @@
*******************************************************************************/
package io.openliberty.tools.langserver.lemminx;
+import io.openliberty.tools.langserver.lemminx.services.LibertyWorkspace;
import org.eclipse.lemminx.services.extensions.IDocumentLinkParticipant;
import org.eclipse.lemminx.services.extensions.codeaction.ICodeActionParticipant;
import org.eclipse.lemminx.services.extensions.completion.ICompletionParticipant;
@@ -69,6 +70,13 @@ public void start(InitializeParams initializeParams, XMLExtensionsRegistry xmlEx
documentLinkParticipant = new LibertyDocumentLinkParticipant();
xmlExtensionsRegistry.registerDocumentLinkParticipant(documentLinkParticipant);
+
+ try {
+ SettingsService.getInstance()
+ .populateAllVariables(LibertyProjectsManager.getInstance().getLibertyWorkspaceFolders());
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
}
@Override
@@ -95,5 +103,17 @@ public void doSave(ISaveContext saveContext) {
SettingsService.getInstance().updateLibertySettings(xmlSettings);
LOGGER.info("Liberty XML settings updated");
}
+ if (saveContext.getType() == SaveContextType.DOCUMENT) {
+ try {
+ LibertyWorkspace workspace = LibertyProjectsManager.getInstance().getWorkspaceFolder(saveContext.getUri());
+ if (workspace != null) {
+ SettingsService.getInstance()
+ .populateVariablesForWorkspace(workspace);
+ }
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ LOGGER.info("Liberty XML variables updated");
+ }
}
}
diff --git a/lemminx-liberty/src/main/java/io/openliberty/tools/langserver/lemminx/LibertyHoverParticipant.java b/lemminx-liberty/src/main/java/io/openliberty/tools/langserver/lemminx/LibertyHoverParticipant.java
index 1e0bdf0d..612fcb4e 100644
--- a/lemminx-liberty/src/main/java/io/openliberty/tools/langserver/lemminx/LibertyHoverParticipant.java
+++ b/lemminx-liberty/src/main/java/io/openliberty/tools/langserver/lemminx/LibertyHoverParticipant.java
@@ -30,12 +30,12 @@
import java.util.ArrayList;
import java.util.Collections;
+import java.util.Iterator;
import java.util.List;
-import java.util.Objects;
import java.util.Optional;
+import java.util.Properties;
import java.util.Set;
import java.util.logging.Logger;
-import java.util.stream.Collectors;
public class LibertyHoverParticipant implements IHoverParticipant {
private static final Logger LOGGER = Logger.getLogger(LibertyHoverParticipant.class.getName());
@@ -47,6 +47,24 @@ public Hover onAttributeName(IHoverRequest request, CancelChecker cancelChecker)
@Override
public Hover onAttributeValue(IHoverRequest request, CancelChecker cancelChecker) {
+ List variables = LibertyUtils.getVariablesFromTextContent(request.getNode().getTextContent());
+ Properties variableMap = SettingsService.getInstance()
+ .getVariablesForServerXml(request.getXMLDocument()
+ .getDocumentURI());
+ StringBuilder stringBuilder = new StringBuilder();
+ Iterator varIter = variables.iterator();
+ while (varIter.hasNext()) {
+ VariableLoc variable = varIter.next();
+ if (variableMap.containsKey(variable.getValue())) {
+ stringBuilder.append(String.format("%s = %s", variable.getValue(), variableMap.get(variable.getValue())));
+ }
+ if (varIter.hasNext()) {
+ stringBuilder.append(System.lineSeparator());
+ }
+ }
+ if (!stringBuilder.isEmpty()) {
+ return new Hover(new MarkupContent("plaintext", stringBuilder.toString()));
+ }
return null;
}
diff --git a/lemminx-liberty/src/main/java/io/openliberty/tools/langserver/lemminx/codeactions/ReplaceVariable.java b/lemminx-liberty/src/main/java/io/openliberty/tools/langserver/lemminx/codeactions/ReplaceVariable.java
new file mode 100644
index 00000000..237d1943
--- /dev/null
+++ b/lemminx-liberty/src/main/java/io/openliberty/tools/langserver/lemminx/codeactions/ReplaceVariable.java
@@ -0,0 +1,77 @@
+/*******************************************************************************
+ * Copyright (c) 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
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package io.openliberty.tools.langserver.lemminx.codeactions;
+
+import com.google.gson.JsonPrimitive;
+import io.openliberty.tools.langserver.lemminx.services.SettingsService;
+import org.eclipse.lemminx.commons.CodeActionFactory;
+import org.eclipse.lemminx.dom.DOMDocument;
+import org.eclipse.lemminx.services.extensions.codeaction.ICodeActionParticipant;
+import org.eclipse.lemminx.services.extensions.codeaction.ICodeActionRequest;
+import org.eclipse.lsp4j.CodeAction;
+import org.eclipse.lsp4j.Diagnostic;
+import org.eclipse.lsp4j.jsonrpc.CancelChecker;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.logging.Logger;
+import java.util.stream.Collectors;
+
+public class ReplaceVariable implements ICodeActionParticipant {
+ private static final Logger LOGGER = Logger.getLogger(ReplaceVariable.class.getName());
+
+ @Override
+ public void doCodeAction(ICodeActionRequest request, List codeActions, CancelChecker cancelChecker) {
+ Diagnostic diagnostic = request.getDiagnostic();
+ DOMDocument document = request.getDocument();
+ try {
+ // Get a list of variables that partially match the specified invalid variables.
+ // Create a code action to replace the invalid variable with each possible valid variable.
+ // First, get the invalid variable.
+ String invalidVariable = null;
+ if (diagnostic.getData() instanceof JsonPrimitive) {
+ invalidVariable = ((JsonPrimitive) diagnostic.getData()).getAsString();
+ }
+ if (diagnostic.getData() instanceof String) {
+ invalidVariable = (String) diagnostic.getData();
+ }
+ final boolean replaceVariable = invalidVariable != null && !invalidVariable.isBlank();
+
+ if (replaceVariable) {
+ Properties existingVariables=SettingsService.getInstance().getVariablesForServerXml(document.getDocumentURI());
+ // filter with entered word -> may not be required
+ String finalInvalidVariable = invalidVariable;
+ Set> filteredVariables = existingVariables
+ .entrySet().stream().filter(entry ->
+ entry.getKey().toString().toLowerCase()
+ .contains(finalInvalidVariable.toLowerCase()))
+ .collect(Collectors.toSet());
+ for (Map.Entry