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>
| ... | ... |
@@ -124,7 +124,7 @@ func (c *containerContext) Command() string {
|
| 124 | 124 |
c.addHeader(commandHeader) |
| 125 | 125 |
command := c.c.Command |
| 126 | 126 |
if c.trunc {
|
| 127 |
- command = stringutils.Truncate(command, 20) |
|
| 127 |
+ command = stringutils.Ellipsis(command, 20) |
|
| 128 | 128 |
} |
| 129 | 129 |
return strconv.Quote(command) |
| 130 | 130 |
} |
| ... | ... |
@@ -200,7 +200,7 @@ func (c *containerContext) Mounts() string {
|
| 200 | 200 |
name = m.Name |
| 201 | 201 |
} |
| 202 | 202 |
if c.trunc {
|
| 203 |
- name = stringutils.Truncate(name, 15) |
|
| 203 |
+ name = stringutils.Ellipsis(name, 15) |
|
| 204 | 204 |
} |
| 205 | 205 |
mounts = append(mounts, name) |
| 206 | 206 |
} |
| ... | ... |
@@ -60,12 +60,12 @@ func TestContainerPsContext(t *testing.T) {
|
| 60 | 60 |
{types.Container{
|
| 61 | 61 |
Mounts: []types.MountPoint{
|
| 62 | 62 |
{
|
| 63 |
- Name: "733908409c91817de8e92b0096373245f329f19a88e2c849f02460e9b3d1c203", |
|
| 63 |
+ Name: "this-is-a-long-volume-name-and-will-be-truncated-if-trunc-is-set", |
|
| 64 | 64 |
Driver: "local", |
| 65 | 65 |
Source: "/a/path", |
| 66 | 66 |
}, |
| 67 | 67 |
}, |
| 68 |
- }, true, "733908409c91817", mountsHeader, ctx.Mounts}, |
|
| 68 |
+ }, true, "this-is-a-lo...", mountsHeader, ctx.Mounts}, |
|
| 69 | 69 |
{types.Container{
|
| 70 | 70 |
Mounts: []types.MountPoint{
|
| 71 | 71 |
{
|
| ... | ... |
@@ -79,8 +79,8 @@ func runHistory(dockerCli *client.DockerCli, opts historyOptions) error {
|
| 79 | 79 |
for _, entry := range history {
|
| 80 | 80 |
imageID = entry.ID |
| 81 | 81 |
createdBy = strings.Replace(entry.CreatedBy, "\t", " ", -1) |
| 82 |
- if opts.noTrunc == false {
|
|
| 83 |
- createdBy = stringutils.Truncate(createdBy, 45) |
|
| 82 |
+ if !opts.noTrunc {
|
|
| 83 |
+ createdBy = stringutils.Ellipsis(createdBy, 45) |
|
| 84 | 84 |
imageID = stringid.TruncateID(entry.ID) |
| 85 | 85 |
} |
| 86 | 86 |
|
| ... | ... |
@@ -109,8 +109,8 @@ func runSearch(dockerCli *client.DockerCli, opts searchOptions) error {
|
| 109 | 109 |
} |
| 110 | 110 |
desc := strings.Replace(res.Description, "\n", " ", -1) |
| 111 | 111 |
desc = strings.Replace(desc, "\r", " ", -1) |
| 112 |
- if !opts.noTrunc && len(desc) > 45 {
|
|
| 113 |
- desc = stringutils.Truncate(desc, 42) + "..." |
|
| 112 |
+ if !opts.noTrunc {
|
|
| 113 |
+ desc = stringutils.Ellipsis(desc, 45) |
|
| 114 | 114 |
} |
| 115 | 115 |
fmt.Fprintf(w, "%s\t%s\t%d\t", res.Name, desc, res.StarCount) |
| 116 | 116 |
if res.IsOfficial {
|
| ... | ... |
@@ -51,8 +51,8 @@ func runList(dockerCli *client.DockerCli, opts listOptions) error {
|
| 51 | 51 |
for _, p := range plugins {
|
| 52 | 52 |
desc := strings.Replace(p.Manifest.Description, "\n", " ", -1) |
| 53 | 53 |
desc = strings.Replace(desc, "\r", " ", -1) |
| 54 |
- if !opts.noTrunc && len(desc) > 45 {
|
|
| 55 |
- desc = stringutils.Truncate(desc, 42) + "..." |
|
| 54 |
+ if !opts.noTrunc {
|
|
| 55 |
+ desc = stringutils.Ellipsis(desc, 45) |
|
| 56 | 56 |
} |
| 57 | 57 |
|
| 58 | 58 |
fmt.Fprintf(w, "%s\t%s\t%s\t%v\n", p.Name, p.Tag, desc, p.Active) |
| ... | ... |
@@ -32,12 +32,26 @@ func GenerateRandomASCIIString(n int) string {
|
| 32 | 32 |
return string(res) |
| 33 | 33 |
} |
| 34 | 34 |
|
| 35 |
+// Ellipsis truncates a string to fit within maxlen, and appends ellipsis (...). |
|
| 36 |
+// For maxlen of 3 and lower, no ellipsis is appended. |
|
| 37 |
+func Ellipsis(s string, maxlen int) string {
|
|
| 38 |
+ r := []rune(s) |
|
| 39 |
+ if len(r) <= maxlen {
|
|
| 40 |
+ return s |
|
| 41 |
+ } |
|
| 42 |
+ if maxlen <= 3 {
|
|
| 43 |
+ return string(r[:maxlen]) |
|
| 44 |
+ } |
|
| 45 |
+ return string(r[:maxlen-3]) + "..." |
|
| 46 |
+} |
|
| 47 |
+ |
|
| 35 | 48 |
// Truncate truncates a string to maxlen. |
| 36 | 49 |
func Truncate(s string, maxlen int) string {
|
| 37 |
- if len(s) <= maxlen {
|
|
| 50 |
+ r := []rune(s) |
|
| 51 |
+ if len(r) <= maxlen {
|
|
| 38 | 52 |
return s |
| 39 | 53 |
} |
| 40 |
- return s[:maxlen] |
|
| 54 |
+ return string(r[:maxlen]) |
|
| 41 | 55 |
} |
| 42 | 56 |
|
| 43 | 57 |
// InSlice tests whether a string is contained in a slice of strings or not. |
| ... | ... |
@@ -57,24 +57,40 @@ func TestGenerateRandomAsciiStringIsAscii(t *testing.T) {
|
| 57 | 57 |
} |
| 58 | 58 |
} |
| 59 | 59 |
|
| 60 |
+func TestEllipsis(t *testing.T) {
|
|
| 61 |
+ str := "t🐳ststring" |
|
| 62 |
+ newstr := Ellipsis(str, 3) |
|
| 63 |
+ if newstr != "t🐳s" {
|
|
| 64 |
+ t.Fatalf("Expected t🐳s, got %s", newstr)
|
|
| 65 |
+ } |
|
| 66 |
+ newstr = Ellipsis(str, 8) |
|
| 67 |
+ if newstr != "t🐳sts..." {
|
|
| 68 |
+ t.Fatalf("Expected tests..., got %s", newstr)
|
|
| 69 |
+ } |
|
| 70 |
+ newstr = Ellipsis(str, 20) |
|
| 71 |
+ if newstr != "t🐳ststring" {
|
|
| 72 |
+ t.Fatalf("Expected t🐳ststring, got %s", newstr)
|
|
| 73 |
+ } |
|
| 74 |
+} |
|
| 75 |
+ |
|
| 60 | 76 |
func TestTruncate(t *testing.T) {
|
| 61 |
- str := "teststring" |
|
| 77 |
+ str := "t🐳ststring" |
|
| 62 | 78 |
newstr := Truncate(str, 4) |
| 63 |
- if newstr != "test" {
|
|
| 64 |
- t.Fatalf("Expected test, got %s", newstr)
|
|
| 79 |
+ if newstr != "t🐳st" {
|
|
| 80 |
+ t.Fatalf("Expected t🐳st, got %s", newstr)
|
|
| 65 | 81 |
} |
| 66 | 82 |
newstr = Truncate(str, 20) |
| 67 |
- if newstr != "teststring" {
|
|
| 68 |
- t.Fatalf("Expected teststring, got %s", newstr)
|
|
| 83 |
+ if newstr != "t🐳ststring" {
|
|
| 84 |
+ t.Fatalf("Expected t🐳ststring, got %s", newstr)
|
|
| 69 | 85 |
} |
| 70 | 86 |
} |
| 71 | 87 |
|
| 72 | 88 |
func TestInSlice(t *testing.T) {
|
| 73 |
- slice := []string{"test", "in", "slice"}
|
|
| 89 |
+ slice := []string{"t🐳st", "in", "slice"}
|
|
| 74 | 90 |
|
| 75 |
- test := InSlice(slice, "test") |
|
| 91 |
+ test := InSlice(slice, "t🐳st") |
|
| 76 | 92 |
if !test {
|
| 77 |
- t.Fatalf("Expected string test to be in slice")
|
|
| 93 |
+ t.Fatalf("Expected string t🐳st to be in slice")
|
|
| 78 | 94 |
} |
| 79 | 95 |
test = InSlice(slice, "SLICE") |
| 80 | 96 |
if !test {
|