diff --git a/main/src/main/scala/sbt/internal/Output.scala b/main/src/main/scala/sbt/internal/Output.scala index 3205b1747..4a0c8c21b 100644 --- a/main/src/main/scala/sbt/internal/Output.scala +++ b/main/src/main/scala/sbt/internal/Output.scala @@ -19,6 +19,7 @@ import Aggregation.{ KeyValue, Values } import Types.idFun import Highlight.{ bold, showMatches } import annotation.tailrec +import sbt.internal.util.EscHelpers import sbt.io.IO @@ -43,7 +44,12 @@ object Output { printLines: Seq[String] => Unit )(using display: Show[ScopedKey[?]]): Unit = { val pattern = Pattern.compile(patternString) - val lines = flatLines(lastLines(keys, streams))(_ flatMap showMatches(pattern)) + val lines = flatLines(lastLines(keys, streams)) { rawLines => + rawLines.flatMap { line => + val stripped = EscHelpers.stripColorsAndMoves(line) + showMatches(pattern)(stripped) + } + } printLines(lines) } @@ -55,8 +61,13 @@ object Output { ): Unit = printLines(grep(tailLines(file, tailDelim), patternString)) - def grep(lines: Seq[String], patternString: String): Seq[String] = - lines.flatMap(showMatches(Pattern.compile(patternString))) + def grep(lines: Seq[String], patternString: String): Seq[String] = { + val pattern = Pattern.compile(patternString) + lines.flatMap { line => + val stripped = EscHelpers.stripColorsAndMoves(line) + showMatches(pattern)(stripped) + } + } def flatLines(outputs: Values[Seq[String]])(f: Seq[String] => Seq[String])(using display: Show[ScopedKey[?]] diff --git a/main/src/test/scala/sbt/internal/OutputSpec.scala b/main/src/test/scala/sbt/internal/OutputSpec.scala new file mode 100644 index 000000000..3d9597fc0 --- /dev/null +++ b/main/src/test/scala/sbt/internal/OutputSpec.scala @@ -0,0 +1,35 @@ +/* + * sbt + * Copyright 2023, Scala center + * Copyright 2011 - 2022, Lightbend, Inc. + * Copyright 2008 - 2010, Mark Harrah + * Licensed under Apache License 2.0 (see LICENSE) + */ + +package sbt.internal + +import scala.Console.{ RED, RESET } +import verify.BasicTestSuite +import sbt.internal.Output.grep + +object OutputSpec extends BasicTestSuite { + + test( + "grep should match pattern against visible text when lines contain ANSI escape sequences (#4840)" + ) { + // Line with ANSI color around "error" - user searching for "error" should find it (strip before match) + val lineWithAnsi = s"${RED}error${RESET}: something failed" + val lines = Seq("info: ok", lineWithAnsi, "warn: deprecated") + val result = grep(lines, "error") + assert(result.size == 1, s"expected 1 match, got ${result.size}: $result") + // Pattern matched the visible "error" (ANSI was stripped before matching); result may have highlight from showMatches + assert(result.head.contains("error"), s"result should contain 'error': ${result.head}") + } + + test("grep should not match when pattern appears only inside ANSI sequence") { + // Line where "error" is not in the visible text (only in escape code - unrealistic but ensures we strip first) + val lines = Seq("info: ok", "something failed") + val result = grep(lines, "error") + assert(result.isEmpty, s"expected no match, got: $result") + } +}