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

hansva pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/hop.git


The following commit(s) were added to refs/heads/main by this push:
     new 633e4cb27b fix error line highlighting, #6245 (#6312)
633e4cb27b is described below

commit 633e4cb27bb8c4e7ec3efb72b0847731440c8388
Author: Hans Van Akelyen <[email protected]>
AuthorDate: Wed Jan 7 21:08:39 2026 +0100

    fix error line highlighting, #6245 (#6312)
    
    * fix error line highlighting, #6245
    
    * Add color coding to console output
---
 core/src/main/java/org/apache/hop/core/Const.java  | 12 +++
 .../core/logging/ConsoleLoggingEventListener.java  | 40 +++++++++-
 .../apache/hop/core/logging/HopLoggingEvent.java   | 29 ++-----
 .../hop/ui/core/widget/LogStyledTextComp.java      | 43 -----------
 .../hop/ui/core/widget/highlight/LogHighlight.java | 88 ----------------------
 .../ui/hopgui/file/pipeline/HopGuiLogBrowser.java  | 60 +++++++++++++--
 .../delegates/HopGuiPipelineLogDelegate.java       | 15 ++--
 .../delegates/HopGuiWorkflowLogDelegate.java       | 15 ++--
 8 files changed, 125 insertions(+), 177 deletions(-)

diff --git a/core/src/main/java/org/apache/hop/core/Const.java 
b/core/src/main/java/org/apache/hop/core/Const.java
index b94bac31ec..49887bfc85 100644
--- a/core/src/main/java/org/apache/hop/core/Const.java
+++ b/core/src/main/java/org/apache/hop/core/Const.java
@@ -517,6 +517,18 @@ public class Const {
       description = "Set this variable to 'Y' to redirect stdout to Hop 
logging.")
   public static final String HOP_REDIRECT_STDOUT = "HOP_REDIRECT_STDOUT";
 
+  /**
+   * System wide flag to enable ANSI color codes in console output. Values: 
'true', 'false', or
+   * 'auto' (default). 'auto' detects if output is to a terminal and enables 
colors only when
+   * appropriate (not when piped to files or log collectors).
+   */
+  @Variable(
+      scope = VariableScope.SYSTEM,
+      value = "auto",
+      description =
+          "Enable ANSI color codes in console output. Values: 'true' (always), 
'false' (never), or 'auto' (only when output is to a terminal)")
+  public static final String HOP_CONSOLE_COLORS = "HOP_CONSOLE_COLORS";
+
   /** System wide flag to log stack traces in a simpler, more human readable 
format */
   @Variable(
       scope = VariableScope.SYSTEM,
diff --git 
a/core/src/main/java/org/apache/hop/core/logging/ConsoleLoggingEventListener.java
 
b/core/src/main/java/org/apache/hop/core/logging/ConsoleLoggingEventListener.java
index c709fbf08d..fd981e4512 100644
--- 
a/core/src/main/java/org/apache/hop/core/logging/ConsoleLoggingEventListener.java
+++ 
b/core/src/main/java/org/apache/hop/core/logging/ConsoleLoggingEventListener.java
@@ -17,20 +17,58 @@
 
 package org.apache.hop.core.logging;
 
+import org.apache.hop.core.Const;
+import org.apache.hop.core.util.EnvUtil;
+
+/**
+ * Console logging event listener that writes log messages to stdout/stderr. 
Supports ANSI color
+ * codes for error messages when configured.
+ */
 public class ConsoleLoggingEventListener implements IHopLoggingEventListener {
 
+  // ANSI color codes for terminal output
+  private static final String ANSI_RESET = "\u001B[0m";
+  private static final String ANSI_RED = "\u001B[31m";
+
   private HopLogLayout layout;
+  private boolean useColors;
 
   public ConsoleLoggingEventListener() {
     this.layout = new HopLogLayout(true);
+
+    // Determine whether to use ANSI color codes based on configuration
+    // Check both system property (-D flag) and environment variable
+    String colorConfig = EnvUtil.getSystemProperty(Const.HOP_CONSOLE_COLORS);
+    if (colorConfig == null) {
+      colorConfig = System.getenv(Const.HOP_CONSOLE_COLORS);
+    }
+    if (colorConfig == null) {
+      colorConfig = "auto"; // default
+    }
+    colorConfig = colorConfig.toLowerCase();
+
+    if ("true".equals(colorConfig)) {
+      // Always use colors
+      this.useColors = true;
+    } else if ("false".equals(colorConfig)) {
+      // Never use colors
+      this.useColors = false;
+    } else {
+      // Auto-detect: only use colors if output is to a terminal (not 
piped/redirected)
+      // System.console() returns null when output is redirected to a file or 
pipe
+      this.useColors = System.console() != null;
+    }
   }
 
   @Override
   public void eventAdded(HopLoggingEvent event) {
-
     String logText = layout.format(event);
 
     if (event.getLevel() == LogLevel.ERROR) {
+      // Apply red color to error messages if colors are enabled
+      if (useColors) {
+        logText = ANSI_RED + logText + ANSI_RESET;
+      }
       HopLogStore.OriginalSystemErr.println(logText);
       HopLogStore.OriginalSystemErr.flush();
     } else {
diff --git 
a/core/src/main/java/org/apache/hop/core/logging/HopLoggingEvent.java 
b/core/src/main/java/org/apache/hop/core/logging/HopLoggingEvent.java
index 466a9944aa..fcb7e853cb 100644
--- a/core/src/main/java/org/apache/hop/core/logging/HopLoggingEvent.java
+++ b/core/src/main/java/org/apache/hop/core/logging/HopLoggingEvent.java
@@ -17,6 +17,11 @@
 
 package org.apache.hop.core.logging;
 
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
 public class HopLoggingEvent {
 
   private Object message;
@@ -35,28 +40,4 @@ public class HopLoggingEvent {
     this.timeStamp = timeStamp;
     this.level = level;
   }
-
-  public Object getMessage() {
-    return message;
-  }
-
-  public void setMessage(Object message) {
-    this.message = message;
-  }
-
-  public long getTimeStamp() {
-    return timeStamp;
-  }
-
-  public void setTimeStamp(long timeStamp) {
-    this.timeStamp = timeStamp;
-  }
-
-  public LogLevel getLevel() {
-    return level;
-  }
-
-  public void setLevel(LogLevel level) {
-    this.level = level;
-  }
 }
diff --git 
a/ui/src/main/java/org/apache/hop/ui/core/widget/LogStyledTextComp.java 
b/ui/src/main/java/org/apache/hop/ui/core/widget/LogStyledTextComp.java
deleted file mode 100644
index e6675f5d54..0000000000
--- a/ui/src/main/java/org/apache/hop/ui/core/widget/LogStyledTextComp.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * 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.hop.ui.core.widget;
-
-import org.apache.hop.core.variables.IVariables;
-import org.apache.hop.ui.core.widget.highlight.LogHighlight;
-import org.apache.hop.ui.util.EnvironmentUtils;
-import org.eclipse.swt.widgets.Composite;
-
-public class LogStyledTextComp extends StyledTextVar {
-  public LogStyledTextComp(IVariables variables, Composite parent, int style) {
-    super(variables, parent, style, false, false);
-  }
-
-  @Override
-  public void addLineStyleListener() {
-    // Only add highlighting in SWT mode (RWT doesn't have StyledText)
-    if (!EnvironmentUtils.getInstance().isWeb()) {
-      addLineStyleListener(new LogHighlight());
-    }
-  }
-
-  @Override
-  public void addLineStyleListener(java.util.List<String> keywords) {
-    // Log highlighting doesn't use keywords, just use default
-    addLineStyleListener();
-  }
-}
diff --git 
a/ui/src/main/java/org/apache/hop/ui/core/widget/highlight/LogHighlight.java 
b/ui/src/main/java/org/apache/hop/ui/core/widget/highlight/LogHighlight.java
deleted file mode 100644
index b53b802742..0000000000
--- a/ui/src/main/java/org/apache/hop/ui/core/widget/highlight/LogHighlight.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * 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.hop.ui.core.widget.highlight;
-
-import org.apache.hop.ui.core.gui.GuiResource;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.custom.LineStyleEvent;
-import org.eclipse.swt.custom.LineStyleListener;
-import org.eclipse.swt.custom.StyleRange;
-
-public class LogHighlight implements LineStyleListener {
-  private static final String[] ERROR_KEYWORDS = {"ERROR", "EXCEPTION", 
"FATAL", "SEVERE"};
-  private static final String[] WARNING_KEYWORDS = {"WARN", "WARNING", 
"CAUTION"};
-
-  private StyleAttribute errorStyle;
-  private StyleAttribute warningStyle;
-
-  public LogHighlight() {
-    initializeStyles();
-  }
-
-  void initializeStyles() {
-    GuiResource resource = GuiResource.getInstance();
-    // Red color for errors
-    errorStyle = new StyleAttribute(GuiResource.getInstance().getColorRed(), 
SWT.NORMAL);
-    // Orange/Yellow color for warnings
-    warningStyle = new 
StyleAttribute(GuiResource.getInstance().getColorOrange(), SWT.NORMAL);
-  }
-
-  @Override
-  public void lineGetStyle(LineStyleEvent event) {
-    String lineText = event.lineText;
-    if (lineText == null || lineText.isEmpty()) {
-      event.styles = new StyleRange[0];
-      return;
-    }
-
-    String upperLine = lineText.toUpperCase();
-
-    // Check for error keywords
-    for (String keyword : ERROR_KEYWORDS) {
-      if (upperLine.contains(keyword)) {
-        StyleRange styleRange =
-            new StyleRange(
-                event.lineOffset,
-                lineText.length(),
-                errorStyle.getForeground(),
-                null,
-                errorStyle.getStyle());
-        event.styles = new StyleRange[] {styleRange};
-        return;
-      }
-    }
-
-    // Check for warning keywords
-    for (String keyword : WARNING_KEYWORDS) {
-      if (upperLine.contains(keyword)) {
-        StyleRange styleRange =
-            new StyleRange(
-                event.lineOffset,
-                lineText.length(),
-                warningStyle.getForeground(),
-                null,
-                warningStyle.getStyle());
-        event.styles = new StyleRange[] {styleRange};
-        return;
-      }
-    }
-
-    // No highlighting needed
-    event.styles = new StyleRange[0];
-  }
-}
diff --git 
a/ui/src/main/java/org/apache/hop/ui/hopgui/file/pipeline/HopGuiLogBrowser.java 
b/ui/src/main/java/org/apache/hop/ui/hopgui/file/pipeline/HopGuiLogBrowser.java
index a5976a99b4..0c376a31fc 100644
--- 
a/ui/src/main/java/org/apache/hop/ui/hopgui/file/pipeline/HopGuiLogBrowser.java
+++ 
b/ui/src/main/java/org/apache/hop/ui/hopgui/file/pipeline/HopGuiLogBrowser.java
@@ -34,6 +34,7 @@ import org.apache.hop.core.logging.HopLoggingEvent;
 import org.apache.hop.core.logging.IHasLogChannel;
 import org.apache.hop.core.logging.ILogChannel;
 import org.apache.hop.core.logging.ILogParentProvided;
+import org.apache.hop.core.logging.LogLevel;
 import org.apache.hop.core.logging.LoggingRegistry;
 import org.apache.hop.core.util.EnvUtil;
 import org.apache.hop.core.util.ExecutorUtil;
@@ -42,9 +43,12 @@ import org.apache.hop.core.variables.DescribedVariable;
 import org.apache.hop.i18n.BaseMessages;
 import org.apache.hop.ui.core.ConstUi;
 import org.apache.hop.ui.core.gui.GuiResource;
+import org.apache.hop.ui.core.widget.StyledTextVar;
 import org.apache.hop.ui.core.widget.TextComposite;
 import org.apache.hop.ui.hopgui.HopGui;
 import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.StyleRange;
+import org.eclipse.swt.custom.StyledText;
 import org.eclipse.swt.events.MouseAdapter;
 import org.eclipse.swt.events.MouseEvent;
 import org.eclipse.swt.events.SelectionAdapter;
@@ -137,23 +141,59 @@ public class HopGuiLogBrowser {
                                   Const.toInt(describedVariable.getValue(), 
Const.MAX_NR_LOG_LINES);
                             }
 
+                            // Get the StyledText widget if available for 
direct style application
+                            StyledText styledText = null;
+                            if (text instanceof StyledTextVar) {
+                              styledText = ((StyledTextVar) 
text).getTextWidget();
+                            }
+
                             synchronized (text) {
                               for (HopLoggingEvent event : logLines) {
                                 String line = logLayout.format(event).trim();
                                 int length = line.length();
 
                                 if (length > 0) {
-                                  // Append by inserting at end
-                                  String currentText = text.getText();
-                                  text.setText(currentText + line + Const.CR);
+                                  boolean isError =
+                                      event.getLevel() != null
+                                          && event.getLevel().getLevel()
+                                              == LogLevel.ERROR.getLevel();
+
+                                  if (styledText != null && 
!styledText.isDisposed()) {
+                                    try {
+                                      // Get the current text length (this is 
where we'll insert)
+                                      int startOffset = 
styledText.getCharCount();
+                                      String textToAdd = line + Const.CR;
+
+                                      // Use replaceTextRange to add text at 
the end
+                                      styledText.replaceTextRange(startOffset, 
0, textToAdd);
+
+                                      // Apply red color directly if this is 
an ERROR level event
+                                      if (isError) {
+                                        StyleRange styleRange = new 
StyleRange();
+                                        styleRange.start = startOffset;
+                                        styleRange.length = line.length();
+                                        styleRange.foreground =
+                                            
GuiResource.getInstance().getColorRed();
+                                        styleRange.fontStyle = SWT.NORMAL;
+                                        styledText.setStyleRange(styleRange);
+                                      }
+                                    } catch (Exception e) {
+                                      // Fallback to setText if there's any 
error
+                                      String currentText = text.getText();
+                                      text.setText(currentText + line + 
Const.CR);
+                                    }
+                                  } else {
+                                    // Fallback for non-StyledText widgets 
(e.g., web mode)
+                                    String currentText = text.getText();
+                                    text.setText(currentText + line + 
Const.CR);
+                                  }
                                 }
                               }
                             }
 
-                            // Erase it all in one go
-                            // This makes it a bit more efficient
-                            String textContent = text.getText();
+                            // Trim old lines if needed to stay within maxSize
                             // Calculate line count
+                            String textContent = text.getText();
                             int size;
                             if (textContent == null || textContent.isEmpty()) {
                               size = 0;
@@ -169,7 +209,13 @@ public class HopGuiLogBrowser {
                             if (maxSize > 0 && size > maxSize) {
                               int dropIndex =
                                   StringUtils.lastOrdinalIndexOf(textContent, 
"\n", maxSize + 1);
-                              text.setText(textContent.substring(dropIndex + 
1));
+                              if (styledText != null && 
!styledText.isDisposed()) {
+                                // Use replaceTextRange to preserve styles on 
remaining text
+                                styledText.replaceTextRange(0, dropIndex + 1, 
"");
+                              } else {
+                                // Fallback for non-StyledText widgets
+                                text.setText(textContent.substring(dropIndex + 
1));
+                              }
                             }
 
                             text.setSelection(text.getCharCount());
diff --git 
a/ui/src/main/java/org/apache/hop/ui/hopgui/file/pipeline/delegates/HopGuiPipelineLogDelegate.java
 
b/ui/src/main/java/org/apache/hop/ui/hopgui/file/pipeline/delegates/HopGuiPipelineLogDelegate.java
index 7ab4fee70b..41b7720563 100644
--- 
a/ui/src/main/java/org/apache/hop/ui/hopgui/file/pipeline/delegates/HopGuiPipelineLogDelegate.java
+++ 
b/ui/src/main/java/org/apache/hop/ui/hopgui/file/pipeline/delegates/HopGuiPipelineLogDelegate.java
@@ -33,9 +33,9 @@ import org.apache.hop.ui.core.PropsUi;
 import org.apache.hop.ui.core.dialog.EnterSelectionDialog;
 import org.apache.hop.ui.core.gui.GuiResource;
 import org.apache.hop.ui.core.gui.GuiToolbarWidgets;
-import org.apache.hop.ui.core.widget.LogStyledTextComp;
 import org.apache.hop.ui.core.widget.OsHelper;
 import org.apache.hop.ui.core.widget.StyledTextComp;
+import org.apache.hop.ui.core.widget.StyledTextVar;
 import org.apache.hop.ui.core.widget.TextComposite;
 import org.apache.hop.ui.hopgui.HopGui;
 import org.apache.hop.ui.hopgui.file.IHopFileTypeHandler;
@@ -116,8 +116,8 @@ public class HopGuiPipelineLogDelegate {
     fd.right = new FormAttachment(100, 0);
     toolbar.setLayoutData(fd);
 
-    // Use StyledTextComp for web (uses Text widget), LogStyledTextComp for 
desktop (with
-    // highlighting)
+    // Use StyledTextComp for web (uses Text widget), StyledTextVar for 
desktop (uses StyledText
+    // for highlighting)
     if (EnvironmentUtils.getInstance().isWeb()) {
       pipelineLogText =
           new StyledTextComp(
@@ -126,12 +126,13 @@ public class HopGuiPipelineLogDelegate {
               SWT.READ_ONLY | SWT.BORDER | SWT.MULTI | SWT.V_SCROLL | 
SWT.H_SCROLL);
     } else {
       pipelineLogText =
-          new LogStyledTextComp(
+          new StyledTextVar(
               pipelineGraph.getVariables(),
               pipelineLogComposite,
-              SWT.READ_ONLY | SWT.BORDER | SWT.MULTI | SWT.V_SCROLL | 
SWT.H_SCROLL);
-      // Add log highlighting (only works in SWT, not RWT)
-      pipelineLogText.addLineStyleListener();
+              SWT.READ_ONLY | SWT.BORDER | SWT.MULTI | SWT.V_SCROLL | 
SWT.H_SCROLL,
+              false,
+              false);
+      // Error highlighting is applied directly in HopGuiLogBrowser when 
adding lines
     }
     PropsUi.setLook(pipelineLogText);
     FormData fdText = new FormData();
diff --git 
a/ui/src/main/java/org/apache/hop/ui/hopgui/file/workflow/delegates/HopGuiWorkflowLogDelegate.java
 
b/ui/src/main/java/org/apache/hop/ui/hopgui/file/workflow/delegates/HopGuiWorkflowLogDelegate.java
index a71f88f659..50964aea83 100644
--- 
a/ui/src/main/java/org/apache/hop/ui/hopgui/file/workflow/delegates/HopGuiWorkflowLogDelegate.java
+++ 
b/ui/src/main/java/org/apache/hop/ui/hopgui/file/workflow/delegates/HopGuiWorkflowLogDelegate.java
@@ -29,9 +29,9 @@ import org.apache.hop.ui.core.PropsUi;
 import org.apache.hop.ui.core.dialog.EnterSelectionDialog;
 import org.apache.hop.ui.core.gui.GuiResource;
 import org.apache.hop.ui.core.gui.GuiToolbarWidgets;
-import org.apache.hop.ui.core.widget.LogStyledTextComp;
 import org.apache.hop.ui.core.widget.OsHelper;
 import org.apache.hop.ui.core.widget.StyledTextComp;
+import org.apache.hop.ui.core.widget.StyledTextVar;
 import org.apache.hop.ui.core.widget.TextComposite;
 import org.apache.hop.ui.hopgui.HopGui;
 import org.apache.hop.ui.hopgui.file.IHopFileTypeHandler;
@@ -117,8 +117,8 @@ public class HopGuiWorkflowLogDelegate {
     fd.right = new FormAttachment(100, 0);
     toolbar.setLayoutData(fd);
 
-    // Use StyledTextComp for web (uses Text widget), LogStyledTextComp for 
desktop (with
-    // highlighting)
+    // Use StyledTextComp for web (uses Text widget), StyledTextVar for 
desktop (uses StyledText
+    // for highlighting)
     if (EnvironmentUtils.getInstance().isWeb()) {
       workflowLogText =
           new StyledTextComp(
@@ -127,12 +127,13 @@ public class HopGuiWorkflowLogDelegate {
               SWT.READ_ONLY | SWT.BORDER | SWT.MULTI | SWT.V_SCROLL | 
SWT.H_SCROLL);
     } else {
       workflowLogText =
-          new LogStyledTextComp(
+          new StyledTextVar(
               workflowGraph.getVariables(),
               workflowLogComposite,
-              SWT.READ_ONLY | SWT.BORDER | SWT.MULTI | SWT.V_SCROLL | 
SWT.H_SCROLL);
-      // Add log highlighting (only works in SWT, not RWT)
-      workflowLogText.addLineStyleListener();
+              SWT.READ_ONLY | SWT.BORDER | SWT.MULTI | SWT.V_SCROLL | 
SWT.H_SCROLL,
+              false,
+              false);
+      // Error highlighting is applied directly in HopGuiLogBrowser when 
adding lines
     }
     PropsUi.setLook(workflowLogText);
     FormData fdText = new FormData();

Reply via email to