This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/main by this push:
new ba4a772c1635 CAMEL-24259: camel-jbang - TUI quick doc for
application.properties in Source tab
ba4a772c1635 is described below
commit ba4a772c163585277dc65f3ae8b785a85b649997
Author: Claus Ibsen <[email protected]>
AuthorDate: Mon Jul 27 09:28:29 2026 +0200
CAMEL-24259: camel-jbang - TUI quick doc for application.properties in
Source tab
Add inline documentation (i key) for application.properties files showing
catalog descriptions for camel.main.*, camel.component.*, camel.language.*,
and camel.dataformat.* properties. Also fix ConfigurationTab to use
version-aware catalog loading matching the running integration.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Signed-off-by: Claus Ibsen <[email protected]>
---
.../jbang/core/commands/tui/ConfigurationTab.java | 18 ++-
.../dsl/jbang/core/commands/tui/SourceTab.java | 137 +++++++++++++++++++++
2 files changed, 152 insertions(+), 3 deletions(-)
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ConfigurationTab.java
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ConfigurationTab.java
index 007a5e7139c5..f0d9634a8539 100644
---
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ConfigurationTab.java
+++
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ConfigurationTab.java
@@ -40,7 +40,7 @@ import dev.tamboui.widgets.table.Cell;
import dev.tamboui.widgets.table.Row;
import dev.tamboui.widgets.table.Table;
import org.apache.camel.catalog.CamelCatalog;
-import org.apache.camel.catalog.DefaultCamelCatalog;
+import org.apache.camel.dsl.jbang.core.common.CatalogLoader;
import org.apache.camel.tooling.model.BaseOptionModel;
import org.apache.camel.tooling.model.ComponentModel;
import org.apache.camel.tooling.model.MainModel;
@@ -57,6 +57,7 @@ class ConfigurationTab extends AbstractTableTab {
private int detailScroll;
private CamelCatalog catalog;
+ private String catalogVersion;
private Map<String, BaseOptionModel> mainOptionsMap;
private final Map<String, Map<String, BaseOptionModel>>
componentOptionsCache = new HashMap<>();
@@ -325,11 +326,22 @@ class ConfigurationTab extends AbstractTableTab {
}
private void initCatalog() {
- if (catalog != null) {
+ IntegrationInfo info = ctx.findSelectedIntegration();
+ String version = info != null ? info.camelVersion : null;
+ if (catalog != null && (version == null ||
version.equals(catalogVersion))) {
+ return;
+ }
+ try {
+ catalog = version != null ? CatalogLoader.loadCatalog(null,
version, true) : null;
+ } catch (Exception e) {
+ catalog = null;
+ }
+ if (catalog == null) {
return;
}
- catalog = new DefaultCamelCatalog();
+ catalogVersion = version;
mainOptionsMap = new HashMap<>();
+ componentOptionsCache.clear();
MainModel mainModel = catalog.mainModel();
if (mainModel != null) {
for (MainModel.MainOptionModel opt : mainModel.getOptions()) {
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SourceTab.java
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SourceTab.java
index 42a7074c67ba..bafa66b66f53 100644
---
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SourceTab.java
+++
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SourceTab.java
@@ -56,6 +56,11 @@ import dev.tamboui.widgets.scrollbar.Scrollbar;
import dev.tamboui.widgets.scrollbar.ScrollbarState;
import org.apache.camel.catalog.CamelCatalog;
import org.apache.camel.dsl.jbang.core.common.CatalogLoader;
+import org.apache.camel.tooling.model.BaseOptionModel;
+import org.apache.camel.tooling.model.ComponentModel;
+import org.apache.camel.tooling.model.DataFormatModel;
+import org.apache.camel.tooling.model.LanguageModel;
+import org.apache.camel.tooling.model.MainModel;
import org.apache.camel.util.json.JsonArray;
import org.apache.camel.util.json.JsonObject;
@@ -80,6 +85,13 @@ class SourceTab extends AbstractTab {
private final Map<String, CamelCatalog> catalogCache = new HashMap<>();
+ // Properties quick-doc caches (invalidated when catalog version changes)
+ private String propsCatalogVersion;
+ private Map<String, BaseOptionModel> mainOptionsCache;
+ private final Map<String, Map<String, BaseOptionModel>>
componentOptionsCache = new HashMap<>();
+ private final Map<String, Map<String, BaseOptionModel>>
languageOptionsCache = new HashMap<>();
+ private final Map<String, Map<String, BaseOptionModel>>
dataformatOptionsCache = new HashMap<>();
+
private static final Pattern YAML_URI_PATTERN = Pattern.compile(
"^\\s*-?\\s*(?:uri|from|to|toD|wireTap|enrich|pollEnrich|deadLetterChannel):\\s*\"?([a-zA-Z][a-zA-Z0-9+.-]*:[^\"\\s]*)");
private static final Pattern YAML_EIP_PATTERN = Pattern.compile(
@@ -467,6 +479,10 @@ class SourceTab extends AbstractTab {
sourceViewer.loadFile(filePath);
if (isCamelSourceFile(filePath)) {
sourceViewer.setQuickDocProvider(this::provideCamelQuickDocs);
+ } else if (isPropertiesFile(filePath)) {
+
sourceViewer.setQuickDocProvider(this::providePropertiesQuickDocs);
+ } else {
+ sourceViewer.setQuickDocProvider(null);
}
focusOnViewer = true;
}
@@ -541,6 +557,127 @@ class SourceTab extends AbstractTab {
return result;
}
+ private static boolean isPropertiesFile(Path path) {
+ return
path.getFileName().toString().toLowerCase().endsWith(".properties");
+ }
+
+ private Map<Integer, List<String>>
providePropertiesQuickDocs(List<JsonObject> codeData) {
+ CamelCatalog catalog = getCatalog();
+ if (catalog == null || codeData.isEmpty()) {
+ return Map.of();
+ }
+ ensureMainOptionsCache(catalog);
+
+ Map<Integer, List<String>> result = new LinkedHashMap<>();
+ for (int i = 0; i < codeData.size(); i++) {
+ String code = codeData.get(i).getString("code");
+ if (code == null) {
+ continue;
+ }
+ String trimmed = code.trim();
+ if (trimmed.isEmpty() || trimmed.startsWith("#") ||
trimmed.startsWith("!")) {
+ continue;
+ }
+ int eq = trimmed.indexOf('=');
+ if (eq <= 0) {
+ continue;
+ }
+ String key = trimmed.substring(0, eq).trim();
+ if (!key.startsWith("camel.")) {
+ continue;
+ }
+ BaseOptionModel opt = lookupPropertyOption(catalog, key);
+ if (opt != null) {
+ String doc = RoutesTab.formatOptionDoc(opt);
+ if (doc != null) {
+ result.put(i, List.of(doc));
+ }
+ }
+ }
+ return result;
+ }
+
+ private BaseOptionModel lookupPropertyOption(CamelCatalog catalog, String
key) {
+ if (mainOptionsCache != null) {
+ BaseOptionModel opt = mainOptionsCache.get(key);
+ if (opt != null) {
+ return opt;
+ }
+ }
+ if (key.startsWith("camel.component.")) {
+ return lookupPrefixedOption(key, "camel.component.",
componentOptionsCache,
+ name -> {
+ ComponentModel m = catalog.componentModel(name);
+ return m != null ? m.getComponentOptions() : null;
+ });
+ }
+ if (key.startsWith("camel.language.")) {
+ return lookupPrefixedOption(key, "camel.language.",
languageOptionsCache,
+ name -> {
+ LanguageModel m = catalog.languageModel(name);
+ return m != null ? m.getOptions() : null;
+ });
+ }
+ if (key.startsWith("camel.dataformat.")) {
+ return lookupPrefixedOption(key, "camel.dataformat.",
dataformatOptionsCache,
+ name -> {
+ DataFormatModel m = catalog.dataFormatModel(name);
+ return m != null ? m.getOptions() : null;
+ });
+ }
+ return null;
+ }
+
+ private BaseOptionModel lookupPrefixedOption(
+ String key, String prefix,
+ Map<String, Map<String, BaseOptionModel>> cache,
+ java.util.function.Function<String, List<? extends
BaseOptionModel>> optionsLoader) {
+ String rest = key.substring(prefix.length());
+ int dot = rest.indexOf('.');
+ if (dot <= 0) {
+ return null;
+ }
+ String name = rest.substring(0, dot);
+ String optionName = rest.substring(dot + 1);
+ Map<String, BaseOptionModel> opts = cache.computeIfAbsent(name, n -> {
+ List<? extends BaseOptionModel> options = optionsLoader.apply(n);
+ if (options == null) {
+ return Map.of();
+ }
+ Map<String, BaseOptionModel> map = new HashMap<>();
+ for (BaseOptionModel o : options) {
+ if (o.getName() != null) {
+ map.put(o.getName(), o);
+ }
+ }
+ return map;
+ });
+ return opts.get(optionName);
+ }
+
+ private void ensureMainOptionsCache(CamelCatalog catalog) {
+ IntegrationInfo info = ctx.findSelectedIntegration();
+ String version = info != null ? info.camelVersion : null;
+ if (version != null && !version.equals(propsCatalogVersion)) {
+ mainOptionsCache = null;
+ componentOptionsCache.clear();
+ languageOptionsCache.clear();
+ dataformatOptionsCache.clear();
+ propsCatalogVersion = version;
+ }
+ if (mainOptionsCache == null) {
+ mainOptionsCache = new HashMap<>();
+ MainModel mainModel = catalog.mainModel();
+ if (mainModel != null) {
+ for (MainModel.MainOptionModel opt : mainModel.getOptions()) {
+ if (opt.getName() != null) {
+ mainOptionsCache.put(opt.getName(), opt);
+ }
+ }
+ }
+ }
+ }
+
private void renderFileList(Frame frame, Rect area) {
Style fileBorderStyle = focusOnViewer ? Theme.muted() :
Style.EMPTY.fg(Theme.accent());
if (entries.isEmpty()) {