gnodet-bot commented on code in PR #26617:
URL: https://github.com/apache/camel/pull/26617#discussion_r4055820183


##########
dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/main/java/org/apache/camel/dsl/yaml/validator/YamlValidator.java:
##########
@@ -214,6 +220,208 @@ static Error parseError(Exception e, String content) {
                 .build();
     }
 
+    private static final Pattern SNAKE_MARK = Pattern.compile("in 'reader', 
line (\\d+), column (\\d+):");
+    private static final Pattern ESCAPE_CHAR = Pattern.compile("found unknown 
escape character (.)\\(");
+    private static final Pattern KEY_LINE = 
Pattern.compile("^\\s*[^-\\s#][^:]*:(\\s|$)");
+
+    /** A position the parser reported: the line and the column it points at, 
both 1-based. */
+    private record Mark(int line, int column) {
+    }
+
+    /**
+     * CAMEL-24837: the snakeyaml messages that only say where the parser gave 
up, said in YAML words. Returns null for
+     * the messages this does not know, so the raw one is still reported.
+     */
+    static Error indentationError(String msg, String[] lines) {
+        List<Mark> marks = marks(msg, lines);
+        if (marks.isEmpty()) {
+            return null;
+        }
+        Mark problem = marks.get(marks.size() - 1);
+        if (msg.contains("expected <block end>, but found '-'")) {
+            return listItemColumn(problem, lines);
+        }
+        if (msg.contains("expected <block end>, but found '<block mapping 
start>'")) {
+            return marks.size() > 1 ? mappingKeyColumn(marks.get(0), problem, 
lines) : null;
+        }
+        if (msg.contains("mapping values are not allowed here")) {
+            return mappingValue(problem, lines);
+        }
+        if (msg.contains("found unknown escape character")) {
+            return unknownEscape(msg, problem, lines);
+        }
+        return null;
+    }
+
+    /** {@code found character '\t(TAB)' that cannot start any token}: the 
line is indented with a tab. */
+    static Error tabIndentation(String msg) {
+        if (!msg.contains("(TAB) for indentation")) {
+            return null;
+        }
+        Matcher m = SNAKE_MARK.matcher(msg);
+        int line = -1;
+        while (m.find()) {
+            line = Integer.parseInt(m.group(1));
+        }
+        return line < 1
+                ? null
+                : hint("line " + line + ": the indentation uses a tab; YAML 
indents with spaces only, replace the"
+                       + " tab with spaces");
+    }
+
+    /**
+     * A list item in a column of its own: the parser only says that it 
expected the end of what it was reading. Name
+     * the list the item belongs to, which is the shallowest list still open 
below the item's column, or the deepest one
+     * above it when the item is the over-indented one.
+     */
+    private static Error listItemColumn(Mark problem, String[] lines) {
+        Mark deeper = null;
+        Mark shallower = null;
+        for (Mark open : openLists(problem, lines)) {
+            if (open.column() > problem.column() && (deeper == null || 
open.column() < deeper.column())) {
+                deeper = open;
+            } else if (open.column() < problem.column() && (shallower == null 
|| open.column() > shallower.column())) {
+                shallower = open;
+            }
+        }
+        Mark list = deeper != null ? deeper : shallower;
+        String belongs = list == null
+                ? "" : ", but the list that starts at line " + list.line() + " 
has its items in column " + list.column();
+        return hint("line " + problem.line() + ": this list item starts in 
column " + problem.column() + belongs
+                    + "; every item of a list must start in the same column");
+    }
+
+    /**
+     * The lists still open above the problem, each as the line and column of 
its first item. A list at column c is open
+     * while every line below it is indented to at least c.
+     */
+    private static List<Mark> openLists(Mark problem, String[] lines) {
+        Map<Integer, Integer> firstItem = new LinkedHashMap<>();
+        int deepest = Integer.MAX_VALUE;
+        for (int i = problem.line() - 2; i >= 0; i--) {
+            String stripped = lines[i].stripLeading();
+            if (stripped.isEmpty() || stripped.startsWith("#")) {
+                continue;
+            }
+            int indent = lines[i].length() - stripped.length();
+            if (stripped.startsWith("- ") && indent <= deepest) {

Review Comment:
   **`startsWith("- ")` misses bare `-` list indicators.**
   
   A list item whose value is on the next line appears as a bare `"-"` in the 
split array:
   ```yaml
   - route:
       steps:
         -
           log:
             message: hi
         - log:         # ← stray column
             message: there
   ```
   Here `lines[i]` would be `"      -"` → `stripped = "-"`, which does not 
start with `"- "`, so the list at that indent is never recorded in `firstItem`. 
The fallback message (column X, no list named) is still produced and is 
correct, but it omits the `"the list that starts at line Y has its items in 
column Z"` context that makes the hint actionable.
   
   Bare list indicators are unusual in Camel DSL routes but they are valid 
YAML. Fix:
   ```suggestion
               if ((stripped.startsWith("- ") || stripped.equals("-")) && 
indent <= deepest) {
   ```



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to