Ver código fonte

Implement stringutils.Ellipsis()

This patch implements an Ellipsis utility to
append an ellipsis (...)  when truncating
strings in output.

It also fixes the existing Truncate() utility
to be compatible with unicode/multibyte characters.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
Sebastiaan van Stijn 9 anos atrás
pai
commit
51dc35cf23

+ 2 - 2
api/client/formatter/container.go

@@ -124,7 +124,7 @@ func (c *containerContext) Command() string {
 	c.addHeader(commandHeader)
 	command := c.c.Command
 	if c.trunc {
-		command = stringutils.Truncate(command, 20)
+		command = stringutils.Ellipsis(command, 20)
 	}
 	return strconv.Quote(command)
 }
@@ -200,7 +200,7 @@ func (c *containerContext) Mounts() string {
 			name = m.Name
 		}
 		if c.trunc {
-			name = stringutils.Truncate(name, 15)
+			name = stringutils.Ellipsis(name, 15)
 		}
 		mounts = append(mounts, name)
 	}

+ 2 - 2
api/client/formatter/container_test.go

@@ -60,12 +60,12 @@ func TestContainerPsContext(t *testing.T) {
 		{types.Container{
 			Mounts: []types.MountPoint{
 				{
-					Name:   "733908409c91817de8e92b0096373245f329f19a88e2c849f02460e9b3d1c203",
+					Name:   "this-is-a-long-volume-name-and-will-be-truncated-if-trunc-is-set",
 					Driver: "local",
 					Source: "/a/path",
 				},
 			},
-		}, true, "733908409c91817", mountsHeader, ctx.Mounts},
+		}, true, "this-is-a-lo...", mountsHeader, ctx.Mounts},
 		{types.Container{
 			Mounts: []types.MountPoint{
 				{

+ 2 - 2
api/client/image/history.go

@@ -79,8 +79,8 @@ func runHistory(dockerCli *client.DockerCli, opts historyOptions) error {
 	for _, entry := range history {
 		imageID = entry.ID
 		createdBy = strings.Replace(entry.CreatedBy, "\t", " ", -1)
-		if opts.noTrunc == false {
-			createdBy = stringutils.Truncate(createdBy, 45)
+		if !opts.noTrunc {
+			createdBy = stringutils.Ellipsis(createdBy, 45)
 			imageID = stringid.TruncateID(entry.ID)
 		}
 

+ 2 - 2
api/client/image/search.go

@@ -109,8 +109,8 @@ func runSearch(dockerCli *client.DockerCli, opts searchOptions) error {
 		}
 		desc := strings.Replace(res.Description, "\n", " ", -1)
 		desc = strings.Replace(desc, "\r", " ", -1)
-		if !opts.noTrunc && len(desc) > 45 {
-			desc = stringutils.Truncate(desc, 42) + "..."
+		if !opts.noTrunc {
+			desc = stringutils.Ellipsis(desc, 45)
 		}
 		fmt.Fprintf(w, "%s\t%s\t%d\t", res.Name, desc, res.StarCount)
 		if res.IsOfficial {

+ 2 - 2
api/client/plugin/list.go

@@ -51,8 +51,8 @@ func runList(dockerCli *client.DockerCli, opts listOptions) error {
 	for _, p := range plugins {
 		desc := strings.Replace(p.Manifest.Description, "\n", " ", -1)
 		desc = strings.Replace(desc, "\r", " ", -1)
-		if !opts.noTrunc && len(desc) > 45 {
-			desc = stringutils.Truncate(desc, 42) + "..."
+		if !opts.noTrunc {
+			desc = stringutils.Ellipsis(desc, 45)
 		}
 
 		fmt.Fprintf(w, "%s\t%s\t%s\t%v\n", p.Name, p.Tag, desc, p.Active)

+ 16 - 2
pkg/stringutils/stringutils.go

@@ -32,12 +32,26 @@ func GenerateRandomASCIIString(n int) string {
 	return string(res)
 }
 
+// Ellipsis truncates a string to fit within maxlen, and appends ellipsis (...).
+// For maxlen of 3 and lower, no ellipsis is appended.
+func Ellipsis(s string, maxlen int) string {
+	r := []rune(s)
+	if len(r) <= maxlen {
+		return s
+	}
+	if maxlen <= 3 {
+		return string(r[:maxlen])
+	}
+	return string(r[:maxlen-3]) + "..."
+}
+
 // Truncate truncates a string to maxlen.
 func Truncate(s string, maxlen int) string {
-	if len(s) <= maxlen {
+	r := []rune(s)
+	if len(r) <= maxlen {
 		return s
 	}
-	return s[:maxlen]
+	return string(r[:maxlen])
 }
 
 // InSlice tests whether a string is contained in a slice of strings or not.

+ 24 - 8
pkg/stringutils/stringutils_test.go

@@ -57,24 +57,40 @@ func TestGenerateRandomAsciiStringIsAscii(t *testing.T) {
 	}
 }
 
+func TestEllipsis(t *testing.T) {
+	str := "t🐳ststring"
+	newstr := Ellipsis(str, 3)
+	if newstr != "t🐳s" {
+		t.Fatalf("Expected t🐳s, got %s", newstr)
+	}
+	newstr = Ellipsis(str, 8)
+	if newstr != "t🐳sts..." {
+		t.Fatalf("Expected tests..., got %s", newstr)
+	}
+	newstr = Ellipsis(str, 20)
+	if newstr != "t🐳ststring" {
+		t.Fatalf("Expected t🐳ststring, got %s", newstr)
+	}
+}
+
 func TestTruncate(t *testing.T) {
-	str := "teststring"
+	str := "t🐳ststring"
 	newstr := Truncate(str, 4)
-	if newstr != "test" {
-		t.Fatalf("Expected test, got %s", newstr)
+	if newstr != "t🐳st" {
+		t.Fatalf("Expected t🐳st, got %s", newstr)
 	}
 	newstr = Truncate(str, 20)
-	if newstr != "teststring" {
-		t.Fatalf("Expected teststring, got %s", newstr)
+	if newstr != "t🐳ststring" {
+		t.Fatalf("Expected t🐳ststring, got %s", newstr)
 	}
 }
 
 func TestInSlice(t *testing.T) {
-	slice := []string{"test", "in", "slice"}
+	slice := []string{"t🐳st", "in", "slice"}
 
-	test := InSlice(slice, "test")
+	test := InSlice(slice, "t🐳st")
 	if !test {
-		t.Fatalf("Expected string test to be in slice")
+		t.Fatalf("Expected string t🐳st to be in slice")
 	}
 	test = InSlice(slice, "SLICE")
 	if !test {