You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2026/08/05 21:18:49 UTC
(camel) 02/03: camel-tui - Fix context detection leaking into nested blocks and offer parameters: completion
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
commit f9dfaff4ca9862af7e7b84eb04f80741863d17ad
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Wed Aug 5 22:48:16 2026 +0200
camel-tui - Fix context detection leaking into nested blocks and offer parameters: completion
- Fix findEnclosingEip skipping through structural keys (from:, steps:,
expression:) and wrongly returning the parent EIP when cursor is nested
inside a structural block
- Offer parameters: and steps: completion when cursor is inside a from:/to:
block that has uri: but no parameters: block yet
- Filter input, outputs, steps from EIP option completions (structural keys)
- Add value completion (boolean/enum) for language and data format options
Co-Authored-By: Claude Opus 4.6 <no...@anthropic.com>
Signed-off-by: Claus Ibsen <cl...@gmail.com>
---
.../dsl/jbang/core/commands/tui/SourceTab.java | 3 +-
.../dsl/jbang/core/commands/tui/SourceViewer.java | 80 ++++++++++++++++++++++
.../core/commands/tui/YamlCompletionTest.java | 3 +-
3 files changed, 84 insertions(+), 2 deletions(-)
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 bc107776263a..bb3bb9a465b6 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
@@ -1086,7 +1086,8 @@ class SourceTab extends AbstractTab {
return items;
}
- private static final Set<String> EIP_BOILERPLATE = Set.of("id", "note", "description", "disabled");
+ private static final Set<String> EIP_BOILERPLATE = Set.of("id", "note", "description", "disabled",
+ "input", "outputs", "steps");
private List<AutocompletePopup.CompletionItem> provideEipKeyCompletions(String contextAfterPrefix) {
CamelCatalog catalog = getCatalog();
diff --git a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SourceViewer.java b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SourceViewer.java
index 93e966bdf096..1b480c4b631c 100644
--- a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SourceViewer.java
+++ b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SourceViewer.java
@@ -827,6 +827,9 @@ class SourceViewer {
record YamlDataFormatOptionContext(String dataFormatName) {
}
+ record YamlEndpointBlockContext(String eipName, boolean consumer, java.util.Set<String> existingKeys) {
+ }
+
record YamlExpressionContext(String eipName) {
}
@@ -889,6 +892,7 @@ class SourceViewer {
}
// walk up to find the parent EIP
+ boolean skippedStructural = false;
for (int i = fromRow; i >= 0; i--) {
String line = editState.getLine(i);
if (line.isBlank()) {
@@ -898,16 +902,74 @@ class SourceViewer {
if (indent < cursorIndent) {
String eipName = extractEipName(line.trim());
if (eipName != null && !STRUCTURAL_KEYS.contains(eipName)) {
+ if (skippedStructural) {
+ return null;
+ }
String camelName = dashToCamelCase(eipName);
return new YamlEipContext(camelName);
}
// keep walking up if we hit a structural key
+ skippedStructural = true;
cursorIndent = indent;
}
}
return null;
}
+ YamlEndpointBlockContext findEndpointBlockContext(int fromRow) {
+ String cursorLine = editState.getLine(fromRow);
+ int cursorIndent = countLeadingSpaces(cursorLine);
+ if (cursorLine.isBlank() && cursorIndent == 0) {
+ int lineCount = editState.lineCount();
+ for (int i = fromRow + 1; i < lineCount; i++) {
+ String next = editState.getLine(i);
+ if (!next.isBlank()) {
+ cursorIndent = countLeadingSpaces(next);
+ break;
+ }
+ }
+ if (cursorIndent == 0) {
+ for (int i = fromRow - 1; i >= 0; i--) {
+ String prev = editState.getLine(i);
+ if (!prev.isBlank()) {
+ cursorIndent = countLeadingSpaces(prev);
+ break;
+ }
+ }
+ }
+ }
+
+ // walk up to find the parent key
+ for (int i = fromRow; i >= 0; i--) {
+ String line = editState.getLine(i);
+ if (line.isBlank()) {
+ continue;
+ }
+ int indent = countLeadingSpaces(line);
+ if (indent < cursorIndent) {
+ String parentKey = extractEipName(line.trim());
+ if (parentKey != null && (CONSUMER_EIPS.contains(parentKey) || PRODUCER_EIPS.contains(parentKey))) {
+ // check siblings: must have uri: and NOT be inside parameters:
+ java.util.Set<String> siblings = collectExistingSiblingKeys(fromRow);
+ if (siblings.contains("uri") && !siblings.contains("parameters")) {
+ // skip if the cursor line itself has a key with colon (not a blank addition)
+ String ct = cursorLine.trim();
+ if (ct.startsWith("- ")) {
+ ct = ct.substring(2).trim();
+ }
+ if (ct.indexOf(':') > 0) {
+ return null;
+ }
+ boolean isConsumer = CONSUMER_EIPS.contains(parentKey);
+ return new YamlEndpointBlockContext(parentKey, isConsumer, siblings);
+ }
+ }
+ break;
+ }
+ }
+ return null;
+ }
+
YamlExpressionContext findExpressionContext(int fromRow) {
// cursor is inside an expression: block — find the parent EIP
String cursorLine = editState.getLine(fromRow);
@@ -1413,6 +1475,24 @@ class SourceViewer {
return;
}
+ // offer parameters: (and steps: for from:) when inside a from:/to: block with uri: but no parameters: yet
+ YamlEndpointBlockContext blockCtx = findEndpointBlockContext(row);
+ if (blockCtx != null && autocompleteProvider != null) {
+ String filter = trimmed;
+ List<AutocompletePopup.CompletionItem> items = new ArrayList<>();
+ items.add(new AutocompletePopup.CompletionItem(
+ "parameters", "Endpoint configuration options", "object",
+ null, false, null, "common", true));
+ if (blockCtx.consumer() && !blockCtx.existingKeys().contains("steps")) {
+ items.add(new AutocompletePopup.CompletionItem(
+ "steps", "Processing steps for this route", "array",
+ null, false, null, "common", true));
+ }
+ autocompletePopup = new AutocompletePopup(items, filter, filter);
+ autocompletePopup.setTitlePrefix(blockCtx.eipName());
+ return;
+ }
+
YamlEndpointContext ctx = findEnclosingComponent(row);
if (ctx != null) {
int colonIdx = trimmed.indexOf(':');
diff --git a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/YamlCompletionTest.java b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/YamlCompletionTest.java
index b7e2105bc4d1..1c3607131703 100644
--- a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/YamlCompletionTest.java
+++ b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/YamlCompletionTest.java
@@ -1596,7 +1596,8 @@ class YamlCompletionTest {
return items;
}
- private static final Set<String> EIP_BOILERPLATE = Set.of("id", "note", "description", "disabled");
+ private static final Set<String> EIP_BOILERPLATE = Set.of("id", "note", "description", "disabled",
+ "input", "outputs", "steps");
private List<AutocompletePopup.CompletionItem> provideEipKeyCompletions(String eipName) {
return provideEipKeyCompletions(eipName, Set.of());