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 <[email protected]> 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 <[email protected]> Signed-off-by: Claus Ibsen <[email protected]> --- .../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());
