Merge pull request #621 from charrywanganthony/displaystringutils

Refactor stringutils and fix docker search output form
Upstream-commit: e4940cb8d2
Component: cli
This commit is contained in:
Sebastiaan van Stijn
2017-10-27 11:08:36 +02:00
committed by GitHub
30 changed files with 4608 additions and 2683 deletions

View File

@ -10,7 +10,6 @@ import (
"github.com/docker/distribution/reference"
"github.com/docker/docker/api/types"
"github.com/docker/docker/pkg/stringid"
"github.com/docker/docker/pkg/stringutils"
"github.com/docker/go-units"
)
@ -165,7 +164,7 @@ func (c *containerContext) Image() string {
func (c *containerContext) Command() string {
command := c.c.Command
if c.trunc {
command = stringutils.Ellipsis(command, 20)
command = Ellipsis(command, 20)
}
return strconv.Quote(command)
}
@ -227,7 +226,7 @@ func (c *containerContext) Mounts() string {
name = m.Name
}
if c.trunc {
name = stringutils.Ellipsis(name, 15)
name = Ellipsis(name, 15)
}
mounts = append(mounts, name)
}

View File

@ -66,7 +66,7 @@ func TestContainerPsContext(t *testing.T) {
Source: "/a/path",
},
},
}, true, "this-is-a-lo...", ctx.Mounts},
}, true, "this-is-a-long…", ctx.Mounts},
{types.Container{
Mounts: []types.MountPoint{
{

View File

@ -0,0 +1,61 @@
package formatter
import (
"unicode/utf8"
"golang.org/x/text/width"
)
// charWidth returns the number of horizontal positions a character occupies,
// and is used to account for wide characters when displaying strings.
//
// In a broad sense, wide characters include East Asian Wide, East Asian Full-width,
// (when not in East Asian context) see http://unicode.org/reports/tr11/.
func charWidth(r rune) int {
switch width.LookupRune(r).Kind() {
case width.EastAsianWide, width.EastAsianFullwidth:
return 2
default:
return 1
}
}
// Ellipsis truncates a string to fit within maxDisplayWidth, and appends ellipsis (…).
// For maxDisplayWidth of 1 and lower, no ellipsis is appended.
// For maxDisplayWidth of 1, first char of string will return even if its width > 1.
func Ellipsis(s string, maxDisplayWidth int) string {
if maxDisplayWidth <= 0 {
return ""
}
rs := []rune(s)
if maxDisplayWidth == 1 {
return string(rs[0])
}
byteLen := len(s)
if byteLen == utf8.RuneCountInString(s) {
if byteLen <= maxDisplayWidth {
return s
}
return string(rs[:maxDisplayWidth-1]) + "…"
}
var (
display []int
displayWidth int
)
for _, r := range rs {
cw := charWidth(r)
displayWidth += cw
display = append(display, displayWidth)
}
if displayWidth <= maxDisplayWidth {
return s
}
for i := range display {
if display[i] <= maxDisplayWidth-1 && display[i+1] > maxDisplayWidth-1 {
return string(rs[:i+1]) + "…"
}
}
return s
}

View File

@ -0,0 +1,30 @@
package formatter
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestEllipsis(t *testing.T) {
var testcases = []struct {
source string
width int
expected string
}{
{source: "t🐳ststring", width: 0, expected: ""},
{source: "t🐳ststring", width: 1, expected: "t"},
{source: "t🐳ststring", width: 2, expected: "t…"},
{source: "t🐳ststring", width: 6, expected: "t🐳st…"},
{source: "t🐳ststring", width: 20, expected: "t🐳ststring"},
{source: "你好世界teststring", width: 0, expected: ""},
{source: "你好世界teststring", width: 1, expected: "你"},
{source: "你好世界teststring", width: 3, expected: "你…"},
{source: "你好世界teststring", width: 6, expected: "你好…"},
{source: "你好世界teststring", width: 20, expected: "你好世界teststring"},
}
for _, testcase := range testcases {
assert.Equal(t, testcase.expected, Ellipsis(testcase.source, testcase.width))
}
}

View File

@ -7,7 +7,6 @@ import (
"github.com/docker/docker/api/types/image"
"github.com/docker/docker/pkg/stringid"
"github.com/docker/docker/pkg/stringutils"
units "github.com/docker/go-units"
)
@ -93,7 +92,7 @@ func (c *historyContext) CreatedSince() string {
func (c *historyContext) CreatedBy() string {
createdBy := strings.Replace(c.h.CreatedBy, "\t", " ", -1)
if c.trunc {
return stringutils.Ellipsis(createdBy, 45)
return Ellipsis(createdBy, 45)
}
return createdBy
}

View File

@ -10,7 +10,6 @@ import (
"github.com/docker/docker/api/types/image"
"github.com/docker/docker/pkg/stringid"
"github.com/docker/docker/pkg/stringutils"
"github.com/stretchr/testify/assert"
)
@ -96,7 +95,7 @@ func TestHistoryContext_CreatedBy(t *testing.T) {
historyContext{
h: image.HistoryResponseItem{CreatedBy: withTabs},
trunc: true,
}, stringutils.Ellipsis(expected, 45), ctx.CreatedBy,
}, Ellipsis(expected, 45), ctx.CreatedBy,
},
}
@ -191,7 +190,7 @@ imageID3 24 hours ago /bin/bash ls
imageID4 24 hours ago /bin/bash grep 183MB Hi
`
expectedTrunc := `IMAGE CREATED CREATED BY SIZE COMMENT
imageID1 24 hours ago /bin/bash ls && npm i && npm run test && k... 183MB Hi
imageID1 24 hours ago /bin/bash ls && npm i && npm run test && kar… 183MB Hi
imageID2 24 hours ago /bin/bash echo 183MB Hi
imageID3 24 hours ago /bin/bash ls 183MB Hi
imageID4 24 hours ago /bin/bash grep 183MB Hi

View File

@ -5,7 +5,6 @@ import (
"github.com/docker/docker/api/types"
"github.com/docker/docker/pkg/stringid"
"github.com/docker/docker/pkg/stringutils"
)
const (
@ -80,7 +79,7 @@ func (c *pluginContext) Description() string {
desc := strings.Replace(c.p.Config.Description, "\n", "", -1)
desc = strings.Replace(desc, "\r", "", -1)
if c.trunc {
desc = stringutils.Ellipsis(desc, 45)
desc = Ellipsis(desc, 45)
}
return desc

View File

@ -5,7 +5,6 @@ import (
"strings"
registry "github.com/docker/docker/api/types/registry"
"github.com/docker/docker/pkg/stringutils"
)
const (
@ -73,7 +72,7 @@ func (c *searchContext) Description() string {
desc := strings.Replace(c.s.Description, "\n", " ", -1)
desc = strings.Replace(desc, "\r", " ", -1)
if c.trunc {
desc = stringutils.Ellipsis(desc, 45)
desc = Ellipsis(desc, 45)
}
return desc
}

View File

@ -7,7 +7,6 @@ import (
"testing"
registrytypes "github.com/docker/docker/api/types/registry"
"github.com/docker/docker/pkg/stringutils"
"github.com/gotestyourself/gotestyourself/golden"
"github.com/stretchr/testify/assert"
)
@ -79,7 +78,7 @@ func TestSearchContextDescription(t *testing.T) {
{searchContext{
s: registrytypes.SearchResult{Description: longDescription},
trunc: true,
}, stringutils.Ellipsis(longDescription, 45), ctx.Description},
}, Ellipsis(longDescription, 45), ctx.Description},
{searchContext{
s: registrytypes.SearchResult{Description: descriptionWReturns},
trunc: false,
@ -87,7 +86,7 @@ func TestSearchContextDescription(t *testing.T) {
{searchContext{
s: registrytypes.SearchResult{Description: descriptionWReturns},
trunc: true,
}, stringutils.Ellipsis(longDescription, 45), ctx.Description},
}, Ellipsis(longDescription, 45), ctx.Description},
}
for _, c := range cases {