From 26010e4c3c089da9588117cff3ea693b158111ec Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pawe=C5=82=20Gronowski?= <pawel.gronowski@docker.com>
Date: Fri, 17 Jan 2025 14:00:46 +0100
Subject: [PATCH] image/tree: Print longest names first and use full width
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

When printing image names, sort them by length and print the longest
first. This also allows them to use a full terminal width because they
are not printed alongside other columns.

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
---
 cli/command/image/tree.go | 20 ++++++++++++++++----
 1 file changed, 16 insertions(+), 4 deletions(-)

diff --git a/cli/command/image/tree.go b/cli/command/image/tree.go
index ac6b209eadf5..7b89e810b059 100644
--- a/cli/command/image/tree.go
+++ b/cli/command/image/tree.go
@@ -281,11 +281,23 @@ func printNames(out *streams.Out, headers []imgColumn, img topImage, color, unta
 		_, _ = fmt.Fprint(out, headers[0].Print(untaggedColor, "<untagged>"))
 	}
 
-	for nameIdx, name := range img.Names {
-		if nameIdx != 0 {
-			_, _ = fmt.Fprintln(out, "")
+	// TODO: Replace with namesLongestToShortest := slices.SortedFunc(slices.Values(img.Names))
+	// once we move to Go 1.23.
+	namesLongestToShortest := make([]string, len(img.Names))
+	copy(namesLongestToShortest, img.Names)
+	sort.Slice(namesLongestToShortest, func(i, j int) bool {
+		return len(namesLongestToShortest[i]) > len(namesLongestToShortest[j])
+	})
+
+	for nameIdx, name := range namesLongestToShortest {
+		// Don't limit first names to the column width because only the last
+		// name will be printed alongside other columns.
+		if nameIdx < len(img.Names)-1 {
+			_, fullWidth := out.GetTtySize()
+			_, _ = fmt.Fprintln(out, color.Apply(truncateRunes(name, int(fullWidth))))
+		} else {
+			_, _ = fmt.Fprint(out, headers[0].Print(color, name))
 		}
-		_, _ = fmt.Fprint(out, headers[0].Print(color, name))
 	}
 }