=UTF-8
diff --git a/org.eclipse.m2e.editor.lemminx.bnd/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.m2e.editor.lemminx.bnd/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000000..23fa13b170
--- /dev/null
+++ b/org.eclipse.m2e.editor.lemminx.bnd/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,9 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=21
+org.eclipse.jdt.core.compiler.compliance=21
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning
+org.eclipse.jdt.core.compiler.release=enabled
+org.eclipse.jdt.core.compiler.source=21
diff --git a/org.eclipse.m2e.editor.lemminx.bnd/META-INF/MANIFEST.MF b/org.eclipse.m2e.editor.lemminx.bnd/META-INF/MANIFEST.MF
new file mode 100644
index 0000000000..f70da9b4e0
--- /dev/null
+++ b/org.eclipse.m2e.editor.lemminx.bnd/META-INF/MANIFEST.MF
@@ -0,0 +1,14 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-SymbolicName: org.eclipse.m2e.editor.lemminx.bnd;singleton:=true
+Bundle-Version: 1.0.0.qualifier
+Require-Bundle: org.eclipse.wildwebdeveloper.xml
+Bundle-Name: %Bundle-Name
+Bundle-Vendor: %Bundle-Vendor
+Automatic-Module-Name: org.eclipse.m2e.editor.lemminx.bnd
+Bundle-RequiredExecutionEnvironment: JavaSE-21
+Import-Package: aQute.bnd.help;version="[2.0.0,3.0.0)",
+ org.eclipse.core.runtime;version="[3.7.0,4.0.0)",
+ org.osgi.framework;version="[1.10.0,2.0.0)",
+ org.osgi.framework.wiring;version="[1.2.0,2.0.0)",
+ org.osgi.resource;version="[1.0.0,2.0.0)"
diff --git a/org.eclipse.m2e.editor.lemminx.bnd/META-INF/services/org.eclipse.lemminx.services.extensions.IXMLExtension b/org.eclipse.m2e.editor.lemminx.bnd/META-INF/services/org.eclipse.lemminx.services.extensions.IXMLExtension
new file mode 100644
index 0000000000..cac61af4e5
--- /dev/null
+++ b/org.eclipse.m2e.editor.lemminx.bnd/META-INF/services/org.eclipse.lemminx.services.extensions.IXMLExtension
@@ -0,0 +1 @@
+org.eclipse.m2e.editor.lemminx.bnd.BndLemminxPlugin
\ No newline at end of file
diff --git a/org.eclipse.m2e.editor.lemminx.bnd/about.html b/org.eclipse.m2e.editor.lemminx.bnd/about.html
new file mode 100644
index 0000000000..8eee37d470
--- /dev/null
+++ b/org.eclipse.m2e.editor.lemminx.bnd/about.html
@@ -0,0 +1,36 @@
+
+
+
+
+About
+
+
+ About This Content
+
+ November 30, 2017
+ License
+
+
+ The Eclipse Foundation makes available all content in this plug-in
+ ("Content"). Unless otherwise indicated below, the Content
+ is provided to you under the terms and conditions of the Eclipse
+ Public License Version 2.0 ("EPL"). A copy of the EPL is
+ available at http://www.eclipse.org/legal/epl-2.0.
+ For purposes of the EPL, "Program" will mean the Content.
+
+
+
+ If you did not receive this Content directly from the Eclipse
+ Foundation, the Content is being redistributed by another party
+ ("Redistributor") and different terms and conditions may
+ apply to your use of any object code in the Content. Check the
+ Redistributor's license that was provided with the Content. If no such
+ license exists, contact the Redistributor. Unless otherwise indicated
+ below, the terms and conditions of the EPL still apply to any source
+ code in the Content and such source code may be obtained at http://www.eclipse.org.
+
+
+
+
diff --git a/org.eclipse.m2e.editor.lemminx.bnd/build.properties b/org.eclipse.m2e.editor.lemminx.bnd/build.properties
new file mode 100644
index 0000000000..2c7f5c7689
--- /dev/null
+++ b/org.eclipse.m2e.editor.lemminx.bnd/build.properties
@@ -0,0 +1,9 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .,\
+ plugin.xml,\
+ about.html,\
+ plugin.properties
+jars.extra.classpath = platform:/plugin/org.eclipse.wildwebdeveloper.xml/language-servers/server/org.eclipse.lemminx-uber.jar
+src.includes = about.html
diff --git a/org.eclipse.m2e.editor.lemminx.bnd/plugin.properties b/org.eclipse.m2e.editor.lemminx.bnd/plugin.properties
new file mode 100644
index 0000000000..28c974808b
--- /dev/null
+++ b/org.eclipse.m2e.editor.lemminx.bnd/plugin.properties
@@ -0,0 +1,14 @@
+#
+# Copyright (c) 2007, 2021 Sonatype, Inc.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License 2.0
+# which accompanies this distribution, and is available at
+# https://www.eclipse.org/legal/epl-2.0/
+#
+# SPDX-License-Identifier: EPL-2.0#
+# Contributors:
+# Sonatype, Inc. - initial API and implementation
+# Rob Newton - added warning preferences page for disabling warnings
+
+Bundle-Vendor = Eclipse.org - m2e
+Bundle-Name = M2E Lemminx Bnd Extension
diff --git a/org.eclipse.m2e.editor.lemminx.bnd/plugin.xml b/org.eclipse.m2e.editor.lemminx.bnd/plugin.xml
new file mode 100644
index 0000000000..dd8bfc85f2
--- /dev/null
+++ b/org.eclipse.m2e.editor.lemminx.bnd/plugin.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
diff --git a/org.eclipse.m2e.editor.lemminx.bnd/src/org/eclipse/m2e/editor/lemminx/bnd/BndClasspathExtensionProvider.java b/org.eclipse.m2e.editor.lemminx.bnd/src/org/eclipse/m2e/editor/lemminx/bnd/BndClasspathExtensionProvider.java
new file mode 100644
index 0000000000..05e6f85118
--- /dev/null
+++ b/org.eclipse.m2e.editor.lemminx.bnd/src/org/eclipse/m2e/editor/lemminx/bnd/BndClasspathExtensionProvider.java
@@ -0,0 +1,78 @@
+/*******************************************************************************
+ * Copyright (c) 2024 Christoph Läubrich and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *******************************************************************************/
+package org.eclipse.m2e.editor.lemminx.bnd;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.eclipse.core.runtime.FileLocator;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.wiring.BundleWire;
+import org.osgi.framework.wiring.BundleWiring;
+
+import aQute.bnd.help.Syntax;
+
+/**
+ * register additional jars and the extension bundle
+ */
+@SuppressWarnings("restriction")
+public class BndClasspathExtensionProvider
+ implements org.eclipse.wildwebdeveloper.xml.LemminxClasspathExtensionProvider {
+
+ @Override
+ public List get() {
+ List list = new ArrayList<>();
+ Set bundleRequirements = new LinkedHashSet<>();
+ bundleRequirements.add((FrameworkUtil.getBundle(getClass())));
+ collectBundles(FrameworkUtil.getBundle(Syntax.class), bundleRequirements);
+ for (Bundle bundle : bundleRequirements) {
+ FileLocator.getBundleFileLocation(bundle).ifPresent(file -> {
+ if (file.isDirectory()) {
+ // For bundles from the workspace launch include the bin folder for classes
+ File outputFolder = new File(file, "bin");
+ if (outputFolder.exists()) {
+ list.add(outputFolder);
+ }
+ }
+ list.add(file);
+ });
+ }
+ return list;
+ }
+
+ private void collectBundles(Bundle bundle, Set bundleRequirements) {
+ if (isValid(bundle) && bundleRequirements.add(bundle)) {
+ BundleWiring wiring = bundle.adapt(BundleWiring.class);
+ List wires = wiring.getRequiredWires("osgi.wiring.package");
+ for (BundleWire bundleWire : wires) {
+ collectBundles(bundleWire.getProvider().getBundle(), bundleRequirements);
+ }
+ }
+
+ }
+
+ private boolean isValid(Bundle bundle) {
+ if (bundle == null) {
+ return false;
+ }
+ String bsn = bundle.getSymbolicName();
+ if ("slf4j.api".equals(bsn)) {
+ // slf4j is already provided
+ return false;
+ }
+ return true;
+ }
+
+}
diff --git a/org.eclipse.m2e.editor.lemminx.bnd/src/org/eclipse/m2e/editor/lemminx/bnd/BndLemminxPlugin.java b/org.eclipse.m2e.editor.lemminx.bnd/src/org/eclipse/m2e/editor/lemminx/bnd/BndLemminxPlugin.java
new file mode 100644
index 0000000000..85fe44ddf2
--- /dev/null
+++ b/org.eclipse.m2e.editor.lemminx.bnd/src/org/eclipse/m2e/editor/lemminx/bnd/BndLemminxPlugin.java
@@ -0,0 +1,132 @@
+/*******************************************************************************
+ * Copyright (c) 2024 Christoph Läubrich and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *******************************************************************************/
+package org.eclipse.m2e.editor.lemminx.bnd;
+
+import java.util.function.Function;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.eclipse.lemminx.dom.DOMDocument;
+import org.eclipse.lemminx.dom.DOMNode;
+import org.eclipse.lemminx.services.extensions.IXMLExtension;
+import org.eclipse.lemminx.services.extensions.XMLExtensionsRegistry;
+import org.eclipse.lemminx.services.extensions.completion.ICompletionParticipant;
+import org.eclipse.lemminx.services.extensions.completion.ICompletionRequest;
+import org.eclipse.lemminx.services.extensions.completion.ICompletionResponse;
+import org.eclipse.lemminx.services.extensions.save.ISaveContext;
+import org.eclipse.lsp4j.CompletionItem;
+import org.eclipse.lsp4j.CompletionItemKind;
+import org.eclipse.lsp4j.InitializeParams;
+import org.eclipse.lsp4j.InsertTextFormat;
+import org.eclipse.lsp4j.jsonrpc.CancelChecker;
+
+import aQute.bnd.help.Syntax;
+
+/**
+ * Extension to provide bnd instruction autocompletion to maven
+ */
+public class BndLemminxPlugin implements IXMLExtension {
+
+ @Override
+ public void start(InitializeParams params, XMLExtensionsRegistry registry) {
+ Logger logger = Logger.getLogger("bnd");
+ logger.log(Level.INFO, "Loading bnd-lemminx extension");
+ registry.registerCompletionParticipant(new ICompletionParticipant() {
+
+ @Override
+ public void onAttributeName(boolean generateValue, ICompletionRequest completionRequest,
+ ICompletionResponse response, CancelChecker checker) throws Exception {
+ }
+
+ @Override
+ public void onAttributeValue(String valuePrefix, ICompletionRequest completionRequest,
+ ICompletionResponse response, CancelChecker checker) throws Exception {
+ }
+
+ @Override
+ public void onDTDSystemId(String valuePrefix, ICompletionRequest completionRequest,
+ ICompletionResponse response, CancelChecker checker) throws Exception {
+ }
+
+ @Override
+ public void onTagOpen(ICompletionRequest completionRequest, ICompletionResponse response,
+ CancelChecker checker) throws Exception {
+ }
+
+ @Override
+ public void onXMLContent(ICompletionRequest completionRequest, ICompletionResponse response,
+ CancelChecker checker) throws Exception {
+ try {
+ DOMDocument xmlDocument = completionRequest.getXMLDocument();
+ DOMNode node = xmlDocument.findNodeBefore(completionRequest.getOffset());
+ logger.log(Level.INFO, "onXMLContent: " + node);
+ if (isBndInstructionNode(node)) {
+ addCompletion(response, syntax -> syntax.getHeader() + ": ");
+ } else if (isFelixInstructionNode(node)) {
+ addCompletion(response, syntax -> {
+ String header = syntax.getHeader();
+ if (header.startsWith("-")) {
+ header = "_" + header.substring(1);
+ }
+ return String.format("<%s>${0}%s>", header, header);
+ });
+ }
+ } catch (Exception e) {
+ logger.log(Level.WARNING, "err=" + e);
+ }
+ }
+
+ private void addCompletion(ICompletionResponse response, Function insert) {
+ Syntax.HELP.values().stream().forEach(syntax -> {
+ CompletionItem item = new CompletionItem();
+ item.setLabel(syntax.getHeader());
+ item.setDocumentation(syntax.getLead());
+ item.setDetail(syntax.getExample());
+ item.setInsertText(insert.apply(syntax));
+ item.setKind(CompletionItemKind.Property);
+ item.setInsertTextFormat(InsertTextFormat.Snippet);
+ response.addCompletionItem(item);
+ });
+ }
+ });
+ }
+
+ private static boolean isBndInstructionNode(DOMNode node) {
+ if (node != null) {
+ if (node.getNodeName().equals("bnd")) {
+ return true;
+ }
+ return isBndInstructionNode(node.getParentNode());
+ }
+ return false;
+ }
+
+ private static boolean isFelixInstructionNode(DOMNode node) {
+ if (node != null) {
+ if (node.getNodeName().equals("instructions")) {
+ return true;
+ }
+ return isFelixInstructionNode(node.getParentNode());
+ }
+ return false;
+ }
+
+ @Override
+ public void stop(XMLExtensionsRegistry registry) {
+ // nothing special to do...
+ }
+
+ @Override
+ public void doSave(ISaveContext context) {
+ IXMLExtension.super.doSave(context);
+ }
+
+}
\ No newline at end of file
diff --git a/org.eclipse.m2e.lemminx.feature/feature.xml b/org.eclipse.m2e.lemminx.feature/feature.xml
index e938a76951..e90c40ee93 100644
--- a/org.eclipse.m2e.lemminx.feature/feature.xml
+++ b/org.eclipse.m2e.lemminx.feature/feature.xml
@@ -2,7 +2,7 @@
@@ -20,4 +20,8 @@
id="org.eclipse.m2e.editor.lemminx"
version="0.0.0"/>
+
+
diff --git a/pom.xml b/pom.xml
index a4930719f5..72678b3b88 100644
--- a/pom.xml
+++ b/pom.xml
@@ -58,6 +58,7 @@
org.eclipse.m2e.sourcelookup
org.eclipse.m2e.sourcelookup.ui
org.eclipse.m2e.editor.lemminx
+ org.eclipse.m2e.editor.lemminx.bnd
org.eclipse.m2e.pde.target
org.eclipse.m2e.pde.ui
org.eclipse.m2e.pde.connector