drccrd commented on code in PR #4005:
URL: 
https://github.com/apache/incubator-kie-tools/pull/4005#discussion_r4062317753


##########
packages/drools-lsp/drools-formatter/src/main/java/org/drools/formatter/LhsFormatter.java:
##########
@@ -0,0 +1,723 @@
+/*
+ * 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.drools.formatter;
+
+import java.util.List;
+
+import org.antlr.v4.runtime.Token;
+import org.drools.drl.parser.antlr4.DRL10Lexer;
+import org.drools.drl.parser.antlr4.DRL10Parser;
+
+/**
+ * Formats a rule's conditions (the when block) and a query's body.
+ *
+ * <p>This class walks the CST with typed {@code visit*} methods:
+ * <ul>
+ *   <li>{@code visitLhs} &rarr; {@code visitLhsExpression} dispatches on
+ *       expression type (and/or/unary/enclosed).</li>
+ *   <li>{@code visitLhsUnary} handles patterns, not/exists, accumulate,
+ *       forall, eval, and groupby.</li>
+ *   <li>{@code visitPatternBind} renders a pattern on one line if it fits
+ *       within {@code options.lineLength()} and its constraint parens hold no 
line
+ *       comment (a {@code //} comment would swallow the rest of a one-line
+ *       rendering); otherwise delegates to {@code emitReflowedPattern} which
+ *       puts each constraint on its own line — trailing comments attached —
+ *       with the closing {@code )} aligned to the opening.</li>
+ *   <li>Parentheses are padded — {@code Person( age &gt; 18 )} — whatever the
+ *       source did, an empty pair staying {@code ()}. The rule is enforced in
+ *       three places because parens reach the output three ways: 
token-by-token
+ *       emission ({@code needsSpaceBetween}), verbatim RHS code
+ *       ({@code rhsNeedsSpace}), and string-built signatures
+ *       ({@code parenthesized}).</li>
+ *   <li>{@code visitAndDef} emits explicit {@code and} keywords between
+ *       patterns in accumulate/groupby source via a {@code nextPatternPrefix}
+ *       mechanism.</li>
+ * </ul>
+ */
+final class LhsFormatter {
+  private final Emitter e;
+
+  // Prefix to prepend to the next pattern (e.g. "and " between patterns)
+  private String nextPatternPrefix = "";
+
+  LhsFormatter(Emitter e) {
+    this.e = e;
+  }
+
+  void visitLhs(DRL10Parser.LhsContext ctx) {
+    e.emitHiddenTokensBefore(ctx);
+    e.emit(e.indent() + "when");
+    e.newline();
+    e.depth++;
+    for (DRL10Parser.LhsExpressionContext expr : ctx.lhsExpression()) {
+      visitLhsExpression(expr);
+    }
+    e.depth--;
+  }
+
+  void visitLhsExpression(DRL10Parser.LhsExpressionContext ctx) {
+    e.emitHiddenTokensBefore(ctx);
+    if (ctx instanceof DRL10Parser.LhsExpressionEnclosedContext enclosed) {
+      emitGroup(consumePatternPrefix(), enclosed.lhsExpression());
+    } else if (!prefixKeyword(ctx).isEmpty()) {
+      e.emit(e.indent() + consumePatternPrefix() + prefixKeyword(ctx));
+      e.newline();
+      e.depth++;
+      visitOperands(ctx);
+      e.depth--;
+    } else if (ctx instanceof DRL10Parser.LhsOrContext orCtx) {
+      List<DRL10Parser.LhsExpressionContext> exprs = orCtx.lhsExpression();
+      for (int i = 0; i < exprs.size(); i++) {
+        if (i > 0) {
+          e.emit(e.indent() + "or");
+          e.newline();
+        }
+        visitLhsExpression(exprs.get(i));
+      }
+    } else if (ctx instanceof DRL10Parser.LhsAndContext andCtx) {
+      List<DRL10Parser.LhsExpressionContext> exprs = andCtx.lhsExpression();
+      for (int i = 0; i < exprs.size(); i++) {
+        if (i > 0) {
+          nextPatternPrefix = "and ";
+        }
+        visitLhsExpression(exprs.get(i));
+      }
+    } else if (ctx instanceof DRL10Parser.LhsUnarySingleContext unary) {
+      visitLhsUnary(unary.lhsUnary());
+    }
+  }
+
+  /**
+   * {@code or} or {@code and} when the element is written in prefix form,
+   * {@code (or A B)}, which is kept rather than rewritten as {@code A or B};
+   * empty otherwise.
+   */
+  private static String prefixKeyword(DRL10Parser.LhsExpressionContext ctx) {
+    int type = ctx.getStart().getType();
+    boolean prefix = (ctx instanceof DRL10Parser.LhsOrContext && type == 
DRL10Lexer.DRL_OR)
+        || (ctx instanceof DRL10Parser.LhsAndContext && type == 
DRL10Lexer.DRL_AND);
+    return prefix ? ctx.getStart().getText() : "";
+  }
+
+  private void visitOperands(DRL10Parser.LhsExpressionContext ctx) {
+    List<DRL10Parser.LhsExpressionContext> operands = ctx instanceof 
DRL10Parser.LhsOrContext orCtx
+        ? orCtx.lhsExpression()
+        : ((DRL10Parser.LhsAndContext) ctx).lhsExpression();
+    for (DRL10Parser.LhsExpressionContext operand : operands) {
+      visitLhsExpression(operand);
+    }
+  }
+
+  /**
+   * {@code head(} and {@code )} on their own lines with the element between
+   * them indented; a prefix-form element keeps its keyword on the opening
+   * line — {@code (or}, {@code not(and} — with its operands beneath.
+   */
+  private void emitGroup(String head, DRL10Parser.LhsExpressionContext inner) {
+    String keyword = prefixKeyword(inner);
+    e.emit(e.indent() + head + "(" + keyword);
+    e.newline();
+    e.depth++;
+    if (keyword.isEmpty()) {
+      visitLhsExpression(inner);
+    } else {
+      visitOperands(inner);
+    }
+    e.depth--;
+    e.emit(e.indent() + ")");
+    e.newline();
+  }
+
+  private void visitLhsUnary(DRL10Parser.LhsUnaryContext ctx) {
+    e.emitHiddenTokensBefore(ctx);
+    if (ctx.lhsPatternBind() != null) {
+      visitPatternBind(ctx.lhsPatternBind());
+    } else {
+      visitConditionalElement(ctx);
+    }
+    emitTrailingInvocations(ctx);
+    if (ctx.SEMI() != null && isOopath(ctx.lhsPatternBind())) {
+      // Before Drools 10 two OOPath lines without a ";" between them were read
+      // as one path, so this one terminator is the author's to keep.
+      e.appendToLastLine(";");
+      e.newline();
+    }
+  }
+
+  private static boolean isOopath(DRL10Parser.LhsPatternBindContext bind) {
+    return bind != null && bind.lhsPattern().stream().anyMatch(p -> 
p.xpathPrimary() != null);
+  }
+
+  private void visitConditionalElement(DRL10Parser.LhsUnaryContext ctx) {
+    // Consume any pending prefix (e.g. "and "): visitPatternBind does this
+    // itself, the other handlers need it here.
+    String prefix = consumePatternPrefix();
+    if (ctx.lhsExists() != null) {
+      visitLhsExists(ctx.lhsExists(), prefix);
+    } else if (ctx.lhsNot() != null) {
+      visitLhsNot(ctx.lhsNot(), prefix);
+    } else if (ctx.lhsEval() != null) {
+      String inner = e.styledText(ctx.lhsEval().conditionalOrExpression());
+      e.emit(e.indent() + prefix
+          + (e.options.parenPadding() ? "eval( " + inner + " )" : "eval(" + 
inner + ")"));
+      e.newline();
+    } else if (ctx.lhsForall() != null) {
+      visitLhsForall(ctx.lhsForall());
+    } else if (ctx.lhsAccumulate() != null) {
+      visitLhsAccumulate(ctx.lhsAccumulate());
+    } else if (ctx.lhsGroupBy() != null) {
+      visitLhsGroupBy(ctx.lhsGroupBy());

Review Comment:
   should definitely have been major! fixed with 
[6c50f68](https://github.com/apache/incubator-kie-tools/pull/4005/commits/6c50f68390d8d5360c54d4620bb99c430c6f2ed8)



-- 
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]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to