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 663ad7e646ccbb54c819c84ba85f2bbae30ad0bb
Author: Claus Ibsen <[email protected]>
AuthorDate: Fri Aug 14 14:49:54 2026 +0200

    camel-tui: Fix completion indent for nested EIPs and add list prefix
    
    Fix deriveInsertionIndent to use the parent EIP scope for correct
    nesting when inserting keys via Tab completion. Keys like steps are
    now placed at the circuitBreaker child level, not under configuration
    blocks like resilience4jConfiguration. Array-type completions (steps)
    now auto-insert the - list item prefix on the child line. Breadcrumb
    skips Configuration keys to match the scope highlighter.
    
    Co-Authored-By: Claude Opus 4.6 <[email protected]>
    Signed-off-by: Claus Ibsen <[email protected]>
---
 .../dsl/jbang/core/commands/tui/SourceViewer.java  | 44 +++++++++++++-
 .../core/commands/tui/YamlCompletionTest.java      | 69 ++++++++++++++++++++++
 2 files changed, 111 insertions(+), 2 deletions(-)

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 b25415b06382..8cdafd29f84d 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
@@ -1185,6 +1185,41 @@ class SourceViewer {
     }
 
     private int deriveInsertionIndent(int fromRow) {
+        // use the scope line (parent EIP) to derive indent for correct nesting
+        int scopeRow = findScopeLineRow(fromRow);
+        if (scopeRow >= 0) {
+            String scopeLine = editState.getLine(scopeRow);
+            int scopeIndent = countLeadingSpaces(scopeLine);
+            String scopeTrimmed = scopeLine.trim();
+            if (scopeTrimmed.startsWith("- ")) {
+                return scopeIndent + 4;
+            }
+            return scopeIndent + 2;
+        }
+        // on a blank line with whitespace, walk up to find the parent EIP at 
lower indent
+        String cursorLine = editState.getLine(fromRow);
+        if (cursorLine.isBlank()) {
+            int wsIndent = cursorLine.length();
+            if (wsIndent > 0) {
+                for (int i = fromRow - 1; i >= 0; i--) {
+                    String line = editState.getLine(i);
+                    if (line.isBlank()) {
+                        continue;
+                    }
+                    int indent = countLeadingSpaces(line);
+                    if (indent < wsIndent) {
+                        String t = line.trim();
+                        if (t.startsWith("- ")) {
+                            t = t.substring(2).trim();
+                        }
+                        if (t.endsWith(":") && 
!STRUCTURAL_KEYS.contains(extractEipName(t))) {
+                            return indent + (line.trim().startsWith("- ") ? 4 
: 2);
+                        }
+                        wsIndent = indent;
+                    }
+                }
+            }
+        }
         return deriveIndentFromPredecessor(fromRow);
     }
 
@@ -1492,7 +1527,8 @@ class SourceViewer {
                 }
                 String key = extractEipName(line.trim());
                 if (key != null) {
-                    if (!BREADCRUMB_SKIP_KEYS.contains(key)) {
+                    if (!BREADCRUMB_SKIP_KEYS.contains(key)
+                            && !key.endsWith("Configuration")) {
                         parts.add(key);
                     }
                     if ("route".equals(key)) {
@@ -1899,7 +1935,11 @@ class SourceViewer {
         } else {
             String prefix = listItem ? "- " : "";
             editState.insert(indentStr + prefix + item.key() + ":");
-            if ("object".equals(item.type()) || "array".equals(item.type())) {
+            if ("array".equals(item.type())) {
+                editState.insert('\n');
+                int childIndent = indent + (listItem ? 4 : 2);
+                editState.insert(" ".repeat(childIndent) + "- ");
+            } else if ("object".equals(item.type())) {
                 editState.insert('\n');
                 editState.insert(indentStr + (listItem ? "    " : "  "));
             } else {
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 fa636d202aab..222c9650c875 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
@@ -1227,6 +1227,75 @@ class YamlCompletionTest {
         assertThat(result).doesNotContain("streaming:\n");
     }
 
+    @Test
+    void insertStepsUnderCircuitBreakerNotUnderConfiguration() throws 
IOException {
+        String yaml = String.join("\n",
+                "- route:",
+                "    from:",
+                "      uri: timer:tick",
+                "      steps:",
+                "        - circuitBreaker:",
+                "            resilience4jConfiguration:",
+                "              failureRateThreshold: 123",
+                "            ",
+                "");
+
+        Path file = tempDir.resolve("route.camel.yaml");
+        Files.writeString(file, yaml);
+
+        SourceViewer viewer = new SourceViewer();
+        viewer.loadFile(file);
+        viewer.enterEditMode();
+        // move cursor to the blank line (line 7) after failureRateThreshold
+        viewer.editState().moveCursorToStart();
+        for (int i = 0; i < 7; i++) {
+            viewer.editState().moveCursorDown();
+        }
+
+        AutocompletePopup.CompletionItem item = new 
AutocompletePopup.CompletionItem(
+                "steps", "Steps", "array", null, false, null, "common");
+        viewer.insertYamlCompletion(item, false, "            ");
+
+        String result = viewer.editState().text();
+        // steps: should be at same indent as resilience4jConfiguration (child 
of circuitBreaker)
+        assertThat(result).contains("            steps:");
+        // NOT at deeper indent under resilience4jConfiguration
+        assertThat(result).doesNotContain("              steps:");
+    }
+
+    @Test
+    void insertStepsAfterEnterAddsListItemPrefix() throws IOException {
+        String yaml = String.join("\n",
+                "- route:",
+                "    from:",
+                "      uri: timer:tick",
+                "      steps:",
+                "        - circuitBreaker:",
+                "            steps:",
+                "              ",
+                "");
+
+        Path file = tempDir.resolve("route.camel.yaml");
+        Files.writeString(file, yaml);
+
+        SourceViewer viewer = new SourceViewer();
+        viewer.loadFile(file);
+        viewer.enterEditMode();
+        viewer.setListItemNodeChecker(key -> "steps".equals(key) || 
"root".equals(key));
+        // move cursor to line 5 (steps:) and press Enter
+        viewer.editState().moveCursorToStart();
+        for (int i = 0; i < 5; i++) {
+            viewer.editState().moveCursorDown();
+        }
+        viewer.editState().moveCursorToLineEnd();
+        viewer.handleKeyEvent(dev.tamboui.tui.event.KeyEvent.ofKey(
+                dev.tamboui.tui.event.KeyCode.ENTER, 
dev.tamboui.tui.event.KeyModifiers.NONE));
+
+        String result = viewer.editState().text();
+        // after steps:, the new line should have "- " list item prefix
+        assertThat(result).contains("            steps:\n              - ");
+    }
+
     // --- Helpers that replicate SourceTab logic for testing ---
 
     private List<AutocompletePopup.CompletionItem> 
provideKeyCompletions(String componentName, String role) {

Reply via email to