This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch feature/CAMEL-25208-code-assist
in repository https://gitbox.apache.org/repos/asf/camel.git

commit eadb58569de383efdd204deb39ecdce51143c41f
Author: Claus Ibsen <[email protected]>
AuthorDate: Wed Sep 30 23:02:30 2026 +0200

    CAMEL-25208: camel-jbang - TUI Source tab completion in the endpoint uris 
of Java routes
    
    Tab in the string of from, to, toD, wireTap, enrich, pollEnrich or poll of
    a Java route file completes the component name, the endpoint options
    (by consumer or producer role, the given ones left out) and their values,
    with the providers the YAML uris have. The string the cursor is in is read
    from the line alone (JavaStringContext), so it works while the file does
    not parse. The completion providers fall back to the CLI's catalog when no
    integration is selected.
    
    The completion popup puts an exact or prefix match first: typing seda
    selected hazelcast-seda before, for YAML as well.
    
    Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
    Signed-off-by: Claus Ibsen <[email protected]>
---
 .../jbang/core/commands/tui/AutocompletePopup.java |  10 ++
 .../jbang/core/commands/tui/JavaStringContext.java |  85 +++++++++++
 .../jbang/core/commands/tui/SourceEditAssist.java  |   6 +-
 .../dsl/jbang/core/commands/tui/SourceTab.java     |   6 +-
 .../dsl/jbang/core/commands/tui/SourceViewer.java  | 103 +++++++++++++
 .../src/main/resources/tui/help/source.md          |   8 +-
 .../commands/tui/JavaStringCompletionTest.java     | 165 +++++++++++++++++++++
 7 files changed, 377 insertions(+), 6 deletions(-)

diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/AutocompletePopup.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/AutocompletePopup.java
index faa66df23bb4..8de140e54531 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/AutocompletePopup.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/AutocompletePopup.java
@@ -17,6 +17,7 @@
 package org.apache.camel.dsl.jbang.core.commands.tui;
 
 import java.util.ArrayList;
+import java.util.Comparator;
 import java.util.List;
 
 import dev.tamboui.layout.Rect;
@@ -535,10 +536,19 @@ class AutocompletePopup {
                     filteredItems.add(item);
                 }
             }
+            // what was typed exactly first, then what starts with it: seda 
before hazelcast-seda
+            filteredItems.sort(Comparator.comparingInt(item -> 
rank(item.key(), f)));
         }
         listState.select(filteredItems.isEmpty() ? null : 0);
     }
 
+    private static int rank(String key, String filter) {
+        if (key.equalsIgnoreCase(filter)) {
+            return 0;
+        }
+        return key.regionMatches(true, 0, filter, 0, filter.length()) ? 1 : 2;
+    }
+
     private static boolean matchesLabel(String group, String filter) {
         if (group == null || group.isEmpty()) {
             return false;
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/JavaStringContext.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/JavaStringContext.java
new file mode 100644
index 000000000000..68613c4c8c92
--- /dev/null
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/JavaStringContext.java
@@ -0,0 +1,85 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.dsl.jbang.core.commands.tui;
+
+import java.util.Set;
+
+/**
+ * The string literal of a Java line the cursor is in, and the method it is an 
argument of: to("kafka:orders?bro| is in
+ * to, with kafka:orders?bro before the cursor. Read from the line alone, so 
it works on a line being typed, which does
+ * not parse (CAMEL-25208).
+ *
+ * @param call   the method the literal is the first text of the arguments of, 
such as to or from
+ * @param before the text of the literal before the cursor
+ */
+record JavaStringContext(String call, String before) {
+
+    /** The DSL methods whose argument is an endpoint uri. */
+    static final Set<String> ENDPOINT_CALLS = Set.of("from", "to", "toD", 
"wireTap", "enrich", "pollEnrich", "poll");
+    /** The ones of them that consume from their endpoint. */
+    static final Set<String> CONSUMER_CALLS = Set.of("from", "pollEnrich", 
"poll");
+
+    /** The context at the cursor, or null when the cursor is not in a string 
literal given to a method. */
+    static JavaStringContext at(String line, int col) {
+        if (line == null || col < 0 || col > line.length()) {
+            return null;
+        }
+        int open = -1;
+        for (int i = 0; i < col; i++) {
+            char c = line.charAt(i);
+            if (open >= 0) {
+                if (c == '\\') {
+                    i++;
+                } else if (c == '"') {
+                    open = -1;
+                }
+            } else if (c == '"') {
+                open = i;
+            } else if (c == '/' && i + 1 < line.length() && line.charAt(i + 1) 
== '/') {
+                // a line comment: no string literal after it
+                return null;
+            }
+        }
+        if (open < 0) {
+            return null;
+        }
+        String head = line.substring(0, open).stripTrailing();
+        if (!head.endsWith("(")) {
+            return null;
+        }
+        head = head.substring(0, head.length() - 1).stripTrailing();
+        int start = head.length();
+        while (start > 0 && Character.isJavaIdentifierPart(head.charAt(start - 
1))) {
+            start--;
+        }
+        String call = head.substring(start);
+        if (call.isEmpty()) {
+            return null;
+        }
+        return new JavaStringContext(call, line.substring(open + 1, col));
+    }
+
+    /** Whether the literal is the endpoint uri of a DSL method. */
+    boolean isEndpoint() {
+        return ENDPOINT_CALLS.contains(call);
+    }
+
+    /** consumer or producer, as the completions of the endpoint options are 
filtered. */
+    String role() {
+        return CONSUMER_CALLS.contains(call) ? "consumer" : "producer";
+    }
+}
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SourceEditAssist.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SourceEditAssist.java
index e3e093424fca..c53c171f4427 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SourceEditAssist.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SourceEditAssist.java
@@ -877,7 +877,7 @@ final class SourceEditAssist {
         if (!context.startsWith("yaml:")) {
             return List.of();
         }
-        CamelCatalog catalog = getCatalog();
+        CamelCatalog catalog = validationCatalog();
         if (catalog == null) {
             return List.of();
         }
@@ -946,7 +946,7 @@ final class SourceEditAssist {
     }
 
     List<AutocompletePopup.CompletionItem> 
provideComponentNameCompletions(String role) {
-        CamelCatalog catalog = getCatalog();
+        CamelCatalog catalog = validationCatalog();
         if (catalog == null) {
             return List.of();
         }
@@ -1231,7 +1231,7 @@ final class SourceEditAssist {
         if (!context.startsWith("yaml:")) {
             return List.of();
         }
-        CamelCatalog catalog = getCatalog();
+        CamelCatalog catalog = validationCatalog();
         if (catalog == null) {
             return List.of();
         }
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 69c3a283a4c1..477cd05b6957 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
@@ -826,6 +826,7 @@ class SourceTab extends AbstractTab {
         boolean routeFile = !SourceEditAssist.isYamlFile(filePath)
                 && (isJavaRouteFile(filePath) || isCamelSourceFile(filePath));
         sourceViewer.setRouteValidator(routeFile ? content -> 
assist.validateRoutes(filePath, content) : null);
+        sourceViewer.setJavaStringCompletion(routeFile && 
filePath.getFileName().toString().endsWith(".java"));
         if (isCamelSourceFile(filePath)) {
             sourceViewer.setQuickDocProvider(assist::provideCamelQuickDocs);
             sourceViewer.setDeprecatedLineScanner(null);
@@ -848,8 +849,9 @@ class SourceTab extends AbstractTab {
             // Java: the quick doc of the routes read into the model by the 
Java DSL parser (CAMEL-25208)
             sourceViewer.setQuickDocProvider(cd -> 
assist.provideRouteQuickDocs(filePath, cd));
             sourceViewer.setDeprecatedLineScanner(null);
-            sourceViewer.setAutocompleteProvider(null);
-            sourceViewer.setAutocompleteValueProvider(null);
+            // Tab in the endpoint uri of from, to, toD...: component names, 
options and values, as YAML uris have
+            
sourceViewer.setAutocompleteProvider(assist::provideYamlKeyCompletions);
+            
sourceViewer.setAutocompleteValueProvider(assist::provideYamlValueCompletions);
             sourceViewer.setEditQuickDocProvider((lines, row) -> 
assist.provideRouteEditQuickDoc(filePath, lines, row));
         } else if (SourceEditAssist.isPropertiesFile(filePath)) {
             
sourceViewer.setQuickDocProvider(assist::providePropertiesQuickDocs);
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 789027b0722f..e3a1dd6e4650 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
@@ -188,6 +188,9 @@ class SourceViewer {
     private EndpointValidator endpointValidator;
     private EndpointValidator simpleValidator;
     private EndpointValidator routeValidator;
+    private boolean javaStringCompletion;
+    /** The completion being chosen in a Java string: its row, the column its 
prefix ends at, the prefix and suffix. */
+    private JavaCompletion javaCompletion;
     private List<String> routeProblems = List.of();
     private List<String> validationErrors;
     private int validationErrorScroll;
@@ -268,6 +271,17 @@ class SourceViewer {
         this.routeValidator = routeValidator;
     }
 
+    /**
+     * Tab completion inside the strings of a Java route file (CAMEL-25208): 
in the endpoint uri of from, to, toD... the
+     * component names, the endpoint options and their values, from the 
completion providers the YAML uris use.
+     */
+    void setJavaStringCompletion(boolean javaStringCompletion) {
+        this.javaStringCompletion = javaStringCompletion;
+    }
+
+    private record JavaCompletion(int row, int endCol, String prefix, String 
suffix) {
+    }
+
     void hide() {
         exitEditMode();
         visible = false;
@@ -280,6 +294,7 @@ class SourceViewer {
         endpointValidator = null;
         simpleValidator = null;
         routeValidator = null;
+        javaStringCompletion = false;
     }
 
     void reset() {
@@ -320,6 +335,7 @@ class SourceViewer {
         endpointValidator = null;
         simpleValidator = null;
         routeValidator = null;
+        javaStringCompletion = false;
     }
 
     boolean isMarkdownMode() {
@@ -995,13 +1011,96 @@ class SourceViewer {
     }
 
     private void openAutocomplete() {
+        javaCompletion = null;
         if (isCamelYamlFile()) {
             openYamlAutocomplete();
+        } else if (javaStringCompletion) {
+            openJavaStringAutocomplete();
         } else {
             openPropertiesAutocomplete();
         }
     }
 
+    /**
+     * The completion of the endpoint uri the cursor is in, in a Java route: 
the component before the colon, an option
+     * after ? or &, its value after =. Read from the line alone, so it works 
while the file does not parse.
+     */
+    private void openJavaStringAutocomplete() {
+        int row = editState.cursorRow();
+        int col = editState.cursorCol();
+        JavaStringContext c = JavaStringContext.at(editState.getLine(row), 
col);
+        if (c == null || !c.isEndpoint() || autocompleteProvider == null) {
+            return;
+        }
+        String before = c.before();
+        int colon = before.indexOf(':');
+        List<AutocompletePopup.CompletionItem> items;
+        String prefix;
+        String suffix;
+        if (colon < 0) {
+            items = autocompleteProvider.provide("yaml-uri:" + c.role());
+            prefix = before;
+            suffix = ":";
+        } else {
+            String scheme = before.substring(0, colon);
+            int q = before.indexOf('?');
+            if (q < 0) {
+                // the path of the uri: what goes there is the component's own
+                return;
+            }
+            int sep = Math.max(before.lastIndexOf('?'), 
before.lastIndexOf('&'));
+            String segment = before.substring(sep + 1);
+            int eq = segment.indexOf('=');
+            if (eq >= 0) {
+                if (autocompleteValueProvider == null) {
+                    return;
+                }
+                items = autocompleteValueProvider.provide("yaml:" + scheme + 
":" + segment.substring(0, eq));
+                prefix = segment.substring(eq + 1);
+                suffix = "";
+            } else {
+                List<String> given = new ArrayList<>();
+                for (String pair : (sep > q ? before.substring(q + 1, sep) : 
"").split("&")) {
+                    if (!pair.isEmpty()) {
+                        given.add(pair.contains("=") ? pair.substring(0, 
pair.indexOf('=')) : pair);
+                    }
+                }
+                items = autocompleteProvider.provide("yaml:" + scheme + ":" + 
c.role() + ":" + String.join(",", given)
+                                                     + "|" + 
before.substring(0, sep));
+                prefix = segment;
+                suffix = "=";
+            }
+        }
+        if (items != null && !items.isEmpty()) {
+            autocompletePopup = new AutocompletePopup(items, prefix, prefix, 
true);
+            if (colon < 0) {
+                autocompletePopup.setTitlePrefix("Components");
+            }
+            javaCompletion = new JavaCompletion(row, col, prefix, suffix);
+        }
+    }
+
+    /** Replaces the prefix the completion was opened on with the chosen item 
and what follows it (: or =). */
+    private void insertJavaCompletion(AutocompletePopup.CompletionItem item) {
+        JavaCompletion jc = javaCompletion;
+        javaCompletion = null;
+        if (editState.cursorRow() != jc.row()) {
+            return;
+        }
+        // the arrows move the cursor within the prefix while the popup is 
open: back to its end
+        int col = editState.cursorCol();
+        for (; col < jc.endCol(); col++) {
+            editState.moveCursorRight();
+        }
+        for (; col > jc.endCol(); col--) {
+            editState.moveCursorLeft();
+        }
+        for (int i = 0; i < jc.prefix().length(); i++) {
+            editState.deleteBackward();
+        }
+        editState.insert(item.key() + jc.suffix());
+    }
+
     private void openPropertiesAutocomplete() {
         String lineText = editState.getLine(editState.cursorRow());
         int col = editState.cursorCol();
@@ -1167,6 +1266,10 @@ class SourceViewer {
 
     private void insertCompletion(AutocompletePopup.CompletionItem item, 
boolean valueMode, boolean listItem) {
         recordEditChange();
+        if (javaCompletion != null) {
+            insertJavaCompletion(item);
+            return;
+        }
         String currentLine = editState.getLine(editState.cursorRow());
         if (isCamelYamlFile()) {
             insertYamlCompletion(item, valueMode, currentLine, listItem);
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/help/source.md 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/help/source.md
index b6eb604a176f..2539d637f9ad 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/help/source.md
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/resources/tui/help/source.md
@@ -72,8 +72,14 @@ Press **F4** to enter edit mode, then **Tab** for 
context-aware completion:
 - Value completion shows enum choices, boolean values, and `{{placeholder}}`
   suggestions from your `.properties` files
 
+**Java DSL routes:**
+- In the endpoint uri of `from`, `to`, `toD`, `wireTap`, `enrich`, `pollEnrich`
+  and `poll`, Tab completes the component name before the `:`, the endpoint
+  options after `?` or `&` (filtered by consumer/producer role, already given
+  ones left out), and the value of an option after `=`
+
 Use **Up/Down** to navigate, **Enter** to accept, **Esc** to dismiss, and
-type to filter the completion list.
+type to filter the completion list (an exact or prefix match comes first).
 
 ## Route Jump Links
 Lines with `to:`, `toD:`, `wireTap:`, or similar endpoints that reference
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/JavaStringCompletionTest.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/JavaStringCompletionTest.java
new file mode 100644
index 000000000000..ad7a28b4e0a2
--- /dev/null
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/JavaStringCompletionTest.java
@@ -0,0 +1,165 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.dsl.jbang.core.commands.tui;
+
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
+
+import dev.tamboui.tui.event.KeyCode;
+import dev.tamboui.tui.event.KeyEvent;
+import dev.tamboui.tui.event.KeyModifiers;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Tab completion in the endpoint uris of a Java route file (CAMEL-25208).
+ */
+class JavaStringCompletionTest {
+
+    @TempDir
+    Path tempDir;
+
+    @BeforeEach
+    void setUp() {
+        Theme.resetForTesting();
+    }
+
+    @Test
+    void theStringTheCursorIsIn() {
+        JavaStringContext c = JavaStringContext.at("        
from(\"timer:tick?per\")", 28);
+        assertThat(c.call()).isEqualTo("from");
+        assertThat(c.before()).isEqualTo("timer:tick?per");
+        assertThat(c.isEndpoint()).isTrue();
+        assertThat(c.role()).isEqualTo("consumer");
+
+        c = JavaStringContext.at("            .to( \"kaf", 21);
+        assertThat(c.call()).isEqualTo("to");
+        assertThat(c.before()).isEqualTo("kaf");
+        assertThat(c.role()).isEqualTo("producer");
+
+        // after the string, in another call, in a comment, not in a string
+        assertThat(JavaStringContext.at("        from(\"timer:tick\").to(", 
30)).isNull();
+        // the second string of a concatenation is not the argument's start
+        assertThat(JavaStringContext.at("        log.info(\"x\" + \"y", 
25)).isNull();
+        assertThat(JavaStringContext.at("        log.info(\"x", 
19).call()).isEqualTo("info");
+        assertThat(JavaStringContext.at("        // to(\"kaf", 18)).isNull();
+        assertThat(JavaStringContext.at("        x = \"kaf", 17)).isNull();
+        // an escaped quote stays in the string
+        assertThat(JavaStringContext.at("        to(\"a\\\"b", 
16).before()).isEqualTo("a\\\"b");
+    }
+
+    private SourceViewer viewer(String src) throws Exception {
+        Path file = tempDir.resolve("MyRoute.java");
+        Files.writeString(file, src, StandardCharsets.UTF_8);
+        AtomicReference<List<IntegrationInfo>> data = new 
AtomicReference<>(List.of());
+        AtomicReference<List<InfraInfo>> infraData = new 
AtomicReference<>(List.of());
+        SourceEditAssist assist = new SourceEditAssist(new 
MonitorContext(data, infraData));
+        SourceViewer viewer = new SourceViewer();
+        viewer.setAutocompleteProvider(assist::provideYamlKeyCompletions);
+        
viewer.setAutocompleteValueProvider(assist::provideYamlValueCompletions);
+        viewer.setJavaStringCompletion(true);
+        viewer.loadFile(file);
+        viewer.enterEditMode();
+        return viewer;
+    }
+
+    /** Puts the cursor on the line, before the given text at its end. */
+    private static void cursorAt(SourceViewer viewer, int row, String tail) {
+        for (int i = 0; i < row; i++) {
+            viewer.handleKeyEvent(KeyEvent.ofKey(KeyCode.DOWN, 
KeyModifiers.NONE));
+        }
+        viewer.handleKeyEvent(KeyEvent.ofKey(KeyCode.END, KeyModifiers.NONE));
+        for (int i = 0; i < tail.length(); i++) {
+            viewer.handleKeyEvent(KeyEvent.ofKey(KeyCode.LEFT, 
KeyModifiers.NONE));
+        }
+    }
+
+    private static void type(SourceViewer viewer, String text) {
+        for (char ch : text.toCharArray()) {
+            viewer.handleKeyEvent(KeyEvent.ofChar(ch, KeyModifiers.NONE));
+        }
+    }
+
+    private static String line(SourceViewer viewer, int row) {
+        return viewer.editText().split("\n")[row];
+    }
+
+    @Test
+    void theComponentOfAnEndpoint() throws Exception {
+        SourceViewer viewer = viewer("""
+                public class MyRoute extends RouteBuilder {
+                    public void configure() {
+                        from("timer:tick")
+                            .to("sed");
+                    }
+                }
+                """);
+        cursorAt(viewer, 3, "\");");
+        viewer.handleKeyEvent(KeyEvent.ofKey(KeyCode.TAB, KeyModifiers.NONE));
+        // the popup filters as the prefix is typed further, then Enter takes 
the selected component
+        type(viewer, "a");
+        viewer.handleKeyEvent(KeyEvent.ofKey(KeyCode.ENTER, 
KeyModifiers.NONE));
+        assertThat(line(viewer, 3)).isEqualTo("            .to(\"seda:\");");
+    }
+
+    @Test
+    void anOptionAndItsValue() throws Exception {
+        SourceViewer viewer = viewer("""
+                public class MyRoute extends RouteBuilder {
+                    public void configure() {
+                        from("timer:tick")
+                            .to("file:out?fileExi");
+                    }
+                }
+                """);
+        cursorAt(viewer, 3, "\");");
+        viewer.handleKeyEvent(KeyEvent.ofKey(KeyCode.TAB, KeyModifiers.NONE));
+        viewer.handleKeyEvent(KeyEvent.ofKey(KeyCode.ENTER, 
KeyModifiers.NONE));
+        assertThat(line(viewer, 3)).isEqualTo("            
.to(\"file:out?fileExist=\");");
+
+        // the cursor is after the = now: the values of the option
+        viewer.handleKeyEvent(KeyEvent.ofKey(KeyCode.TAB, KeyModifiers.NONE));
+        type(viewer, "Appe");
+        viewer.handleKeyEvent(KeyEvent.ofKey(KeyCode.ENTER, 
KeyModifiers.NONE));
+        assertThat(line(viewer, 3)).isEqualTo("            
.to(\"file:out?fileExist=Append\");");
+    }
+
+    @Test
+    void noCompletionOutsideAnEndpoint() throws Exception {
+        SourceViewer viewer = viewer("""
+                public class MyRoute extends RouteBuilder {
+                    public void configure() {
+                        from("timer:tick")
+                            .log("sed");
+                    }
+                }
+                """);
+        String before = viewer.editText();
+        cursorAt(viewer, 3, "\");");
+        viewer.handleKeyEvent(KeyEvent.ofKey(KeyCode.TAB, KeyModifiers.NONE));
+        viewer.handleKeyEvent(KeyEvent.ofKey(KeyCode.ENTER, 
KeyModifiers.NONE));
+        // no popup: Enter splits the line as in any editor
+        assertThat(viewer.editText()).isNotEqualTo(before);
+        assertThat(viewer.editText()).doesNotContain("seda:");
+    }
+}

Reply via email to