From 92d4cfa69005eefed675d89193673f4b410cd5b7 Mon Sep 17 00:00:00 2001 From: Friendseeker <66892505+Friendseeker@users.noreply.github.com> Date: Tue, 29 Oct 2024 17:43:33 -0700 Subject: [PATCH 1/2] Replace nnbsp with regular space --- .../src/main/scala/sbt/internal/client/NetworkClient.scala | 2 +- main/src/main/scala/sbt/internal/Aggregation.scala | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/main-command/src/main/scala/sbt/internal/client/NetworkClient.scala b/main-command/src/main/scala/sbt/internal/client/NetworkClient.scala index e31891564..505d1a1c9 100644 --- a/main-command/src/main/scala/sbt/internal/client/NetworkClient.scala +++ b/main-command/src/main/scala/sbt/internal/client/NetworkClient.scala @@ -1011,7 +1011,7 @@ class NetworkClient( private def timing(startTime: Long, endTime: Long): String = { import java.text.DateFormat val format = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM) - val nowString = format.format(new java.util.Date(endTime)) + val nowString = format.format(new java.util.Date(endTime)).replace("\u202F", "\u0020") val total = math.max(0, (endTime - startTime + 500) / 1000) val totalString = s"$total s" + (if (total <= 60) "" diff --git a/main/src/main/scala/sbt/internal/Aggregation.scala b/main/src/main/scala/sbt/internal/Aggregation.scala index 8a26dcd77..767d0acc8 100644 --- a/main/src/main/scala/sbt/internal/Aggregation.scala +++ b/main/src/main/scala/sbt/internal/Aggregation.scala @@ -152,7 +152,7 @@ object Aggregation { } def timing(format: java.text.DateFormat, startTime: Long, endTime: Long): String = { - val nowString = format.format(new java.util.Date(endTime)) + val nowString = format.format(new java.util.Date(endTime)).replace("\u202F", "\u0020") val total = (endTime - startTime + 500) / 1000 val totalString = s"$total s" + (if (total <= 60) "" From fae2bcc01b3f8a1afeea1a6240d7f721cde17193 Mon Sep 17 00:00:00 2001 From: friendseeker <66892505+Friendseeker@users.noreply.github.com> Date: Tue, 29 Oct 2024 21:40:51 -0700 Subject: [PATCH 2/2] Add documentation & deduplicate implementation --- .../sbt/internal/client/NetworkClient.scala | 50 +++++++++++-------- .../main/scala/sbt/internal/Aggregation.scala | 16 +----- .../scala/sbt/internal/AggregationSpec.scala | 4 ++ 3 files changed, 35 insertions(+), 35 deletions(-) diff --git a/main-command/src/main/scala/sbt/internal/client/NetworkClient.scala b/main-command/src/main/scala/sbt/internal/client/NetworkClient.scala index 505d1a1c9..45ec9b52c 100644 --- a/main-command/src/main/scala/sbt/internal/client/NetworkClient.scala +++ b/main-command/src/main/scala/sbt/internal/client/NetworkClient.scala @@ -15,8 +15,10 @@ import java.lang.ProcessBuilder.Redirect import java.net.{ Socket, SocketException } import java.nio.file.Files import java.util.UUID +import java.util.Date import java.util.concurrent.atomic.{ AtomicBoolean, AtomicReference } import java.util.concurrent.{ ConcurrentHashMap, LinkedBlockingQueue, TimeUnit } +import java.text.DateFormat import sbt.BasicCommandStrings.{ DashDashDetachStdio, DashDashServer, Shutdown, TerminateAction } import sbt.internal.client.NetworkClient.Arguments @@ -532,7 +534,7 @@ class NetworkClient( case null => case (q, startTime, name) => val now = System.currentTimeMillis - val message = timing(startTime, now) + val message = NetworkClient.timing(startTime, now) val ec = exitCode if (batchMode.get || !attached.get) { if (ec == 0) console.success(message) @@ -1006,26 +1008,6 @@ class NetworkClient( RawInputThread.this.interrupt() } } - - // copied from Aggregation - private def timing(startTime: Long, endTime: Long): String = { - import java.text.DateFormat - val format = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM) - val nowString = format.format(new java.util.Date(endTime)).replace("\u202F", "\u0020") - val total = math.max(0, (endTime - startTime + 500) / 1000) - val totalString = s"$total s" + - (if (total <= 60) "" - else { - val maybeHours = total / 3600 match { - case 0 => "" - case h => f"$h%02d:" - } - val mins = f"${total % 3600 / 60}%02d" - val secs = f"${total % 60}%02d" - s" ($maybeHours$mins:$secs)" - }) - s"Total time: $totalString, completed $nowString" - } } object NetworkClient { @@ -1138,6 +1120,32 @@ object NetworkClient { ) } + private[sbt] def timing(format: DateFormat, startTime: Long, endTime: Long): String = { + // sbt#7558 + // JDK 20+ emits special space (NNBSP) as part of formatted date + // Which sometimes becomes garbled in standard output + // Therefore we replace NNBSP (u202f) with standard space (u0020) + val nowString = format.format(new Date(endTime)).replace("\u202F", "\u0020") + val total = (endTime - startTime + 500) / 1000 + val totalString = s"$total s" + + (if (total <= 60) "" + else { + val maybeHours = total / 3600 match { + case 0 => "" + case h => f"$h%02d:" + } + val mins = f"${total % 3600 / 60}%02d" + val secs = f"${total % 60}%02d" + s" ($maybeHours$mins:$secs)" + }) + s"Total time: $totalString, completed $nowString" + } + + private[sbt] def timing(startTime: Long, endTime: Long): String = { + val format = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM) + timing(format, startTime, endTime) + } + def client( baseDirectory: File, args: Array[String], diff --git a/main/src/main/scala/sbt/internal/Aggregation.scala b/main/src/main/scala/sbt/internal/Aggregation.scala index 767d0acc8..311e1beb1 100644 --- a/main/src/main/scala/sbt/internal/Aggregation.scala +++ b/main/src/main/scala/sbt/internal/Aggregation.scala @@ -17,6 +17,7 @@ import sbt.SlashSyntax0._ import sbt.internal.util.complete.Parser import sbt.internal.util.complete.Parser.{ failure, seq, success } import sbt.internal.util._ +import sbt.internal.client.NetworkClient import sbt.std.Transform.DummyTaskMap import sbt.util.{ Logger, Show } import scala.annotation.nowarn @@ -152,20 +153,7 @@ object Aggregation { } def timing(format: java.text.DateFormat, startTime: Long, endTime: Long): String = { - val nowString = format.format(new java.util.Date(endTime)).replace("\u202F", "\u0020") - val total = (endTime - startTime + 500) / 1000 - val totalString = s"$total s" + - (if (total <= 60) "" - else { - val maybeHours = total / 3600 match { - case 0 => "" - case h => f"$h%02d:" - } - val mins = f"${total % 3600 / 60}%02d" - val secs = f"${total % 60}%02d" - s" ($maybeHours$mins:$secs)" - }) - s"Total time: $totalString, completed $nowString" + NetworkClient.timing(format, startTime, endTime) } def defaultFormat: DateFormat = { diff --git a/main/src/test/scala/sbt/internal/AggregationSpec.scala b/main/src/test/scala/sbt/internal/AggregationSpec.scala index 9acf09dc3..8ac2d8f8a 100644 --- a/main/src/test/scala/sbt/internal/AggregationSpec.scala +++ b/main/src/test/scala/sbt/internal/AggregationSpec.scala @@ -22,4 +22,8 @@ object AggregationSpec extends verify.BasicTestSuite { assert(timing(6003099).startsWith("Total time: 6003 s (01:40:03),")) assert(timing(96003099).startsWith("Total time: 96003 s (26:40:03),")) } + + test("timing should not emit special space characters") { + assert(!timing(96003099).contains("\u202F")) + } }