vendor: add tablewriter
This commit is contained in:
parent
7b19a0b4e8
commit
66f1d243d1
|
@ -131,3 +131,5 @@ a0175ee3bccc567396460bf5acd36800cb10c49c github.com/alecthomas/template/parse
|
|||
4d65901933bb7bed40783d9b9f6ae2ea2b829889 (dirty) github.com/Xe/x/tools/svc/credentials/jwt
|
||||
da118f7b8e5954f39d0d2130ab35d4bf0e3cb344 golang.org/x/net/context
|
||||
0eb507a2ca07f13baf499f89d66cc566bf644643 (dirty) google.golang.org/grpc/credentials
|
||||
737072b4e32b7a5018b4a7125da8d12de90e8045 github.com/mattn/go-runewidth
|
||||
44e365d423f4f06769182abfeeae2b91be9d529b github.com/olekukonko/tablewriter
|
||||
|
|
|
@ -0,0 +1,481 @@
|
|||
package runewidth
|
||||
|
||||
var (
|
||||
// EastAsianWidth will be set true if the current locale is CJK
|
||||
EastAsianWidth = IsEastAsian()
|
||||
|
||||
// DefaultCondition is a condition in current locale
|
||||
DefaultCondition = &Condition{EastAsianWidth}
|
||||
)
|
||||
|
||||
type interval struct {
|
||||
first rune
|
||||
last rune
|
||||
}
|
||||
|
||||
var combining = []interval{
|
||||
{0x0300, 0x036F}, {0x0483, 0x0486}, {0x0488, 0x0489},
|
||||
{0x0591, 0x05BD}, {0x05BF, 0x05BF}, {0x05C1, 0x05C2},
|
||||
{0x05C4, 0x05C5}, {0x05C7, 0x05C7}, {0x0600, 0x0603},
|
||||
{0x0610, 0x0615}, {0x064B, 0x065E}, {0x0670, 0x0670},
|
||||
{0x06D6, 0x06E4}, {0x06E7, 0x06E8}, {0x06EA, 0x06ED},
|
||||
{0x070F, 0x070F}, {0x0711, 0x0711}, {0x0730, 0x074A},
|
||||
{0x07A6, 0x07B0}, {0x07EB, 0x07F3}, {0x0901, 0x0902},
|
||||
{0x093C, 0x093C}, {0x0941, 0x0948}, {0x094D, 0x094D},
|
||||
{0x0951, 0x0954}, {0x0962, 0x0963}, {0x0981, 0x0981},
|
||||
{0x09BC, 0x09BC}, {0x09C1, 0x09C4}, {0x09CD, 0x09CD},
|
||||
{0x09E2, 0x09E3}, {0x0A01, 0x0A02}, {0x0A3C, 0x0A3C},
|
||||
{0x0A41, 0x0A42}, {0x0A47, 0x0A48}, {0x0A4B, 0x0A4D},
|
||||
{0x0A70, 0x0A71}, {0x0A81, 0x0A82}, {0x0ABC, 0x0ABC},
|
||||
{0x0AC1, 0x0AC5}, {0x0AC7, 0x0AC8}, {0x0ACD, 0x0ACD},
|
||||
{0x0AE2, 0x0AE3}, {0x0B01, 0x0B01}, {0x0B3C, 0x0B3C},
|
||||
{0x0B3F, 0x0B3F}, {0x0B41, 0x0B43}, {0x0B4D, 0x0B4D},
|
||||
{0x0B56, 0x0B56}, {0x0B82, 0x0B82}, {0x0BC0, 0x0BC0},
|
||||
{0x0BCD, 0x0BCD}, {0x0C3E, 0x0C40}, {0x0C46, 0x0C48},
|
||||
{0x0C4A, 0x0C4D}, {0x0C55, 0x0C56}, {0x0CBC, 0x0CBC},
|
||||
{0x0CBF, 0x0CBF}, {0x0CC6, 0x0CC6}, {0x0CCC, 0x0CCD},
|
||||
{0x0CE2, 0x0CE3}, {0x0D41, 0x0D43}, {0x0D4D, 0x0D4D},
|
||||
{0x0DCA, 0x0DCA}, {0x0DD2, 0x0DD4}, {0x0DD6, 0x0DD6},
|
||||
{0x0E31, 0x0E31}, {0x0E34, 0x0E3A}, {0x0E47, 0x0E4E},
|
||||
{0x0EB1, 0x0EB1}, {0x0EB4, 0x0EB9}, {0x0EBB, 0x0EBC},
|
||||
{0x0EC8, 0x0ECD}, {0x0F18, 0x0F19}, {0x0F35, 0x0F35},
|
||||
{0x0F37, 0x0F37}, {0x0F39, 0x0F39}, {0x0F71, 0x0F7E},
|
||||
{0x0F80, 0x0F84}, {0x0F86, 0x0F87}, {0x0F90, 0x0F97},
|
||||
{0x0F99, 0x0FBC}, {0x0FC6, 0x0FC6}, {0x102D, 0x1030},
|
||||
{0x1032, 0x1032}, {0x1036, 0x1037}, {0x1039, 0x1039},
|
||||
{0x1058, 0x1059}, {0x1160, 0x11FF}, {0x135F, 0x135F},
|
||||
{0x1712, 0x1714}, {0x1732, 0x1734}, {0x1752, 0x1753},
|
||||
{0x1772, 0x1773}, {0x17B4, 0x17B5}, {0x17B7, 0x17BD},
|
||||
{0x17C6, 0x17C6}, {0x17C9, 0x17D3}, {0x17DD, 0x17DD},
|
||||
{0x180B, 0x180D}, {0x18A9, 0x18A9}, {0x1920, 0x1922},
|
||||
{0x1927, 0x1928}, {0x1932, 0x1932}, {0x1939, 0x193B},
|
||||
{0x1A17, 0x1A18}, {0x1B00, 0x1B03}, {0x1B34, 0x1B34},
|
||||
{0x1B36, 0x1B3A}, {0x1B3C, 0x1B3C}, {0x1B42, 0x1B42},
|
||||
{0x1B6B, 0x1B73}, {0x1DC0, 0x1DCA}, {0x1DFE, 0x1DFF},
|
||||
{0x200B, 0x200F}, {0x202A, 0x202E}, {0x2060, 0x2063},
|
||||
{0x206A, 0x206F}, {0x20D0, 0x20EF}, {0x302A, 0x302F},
|
||||
{0x3099, 0x309A}, {0xA806, 0xA806}, {0xA80B, 0xA80B},
|
||||
{0xA825, 0xA826}, {0xFB1E, 0xFB1E}, {0xFE00, 0xFE0F},
|
||||
{0xFE20, 0xFE23}, {0xFEFF, 0xFEFF}, {0xFFF9, 0xFFFB},
|
||||
{0x10A01, 0x10A03}, {0x10A05, 0x10A06}, {0x10A0C, 0x10A0F},
|
||||
{0x10A38, 0x10A3A}, {0x10A3F, 0x10A3F}, {0x1D167, 0x1D169},
|
||||
{0x1D173, 0x1D182}, {0x1D185, 0x1D18B}, {0x1D1AA, 0x1D1AD},
|
||||
{0x1D242, 0x1D244}, {0xE0001, 0xE0001}, {0xE0020, 0xE007F},
|
||||
{0xE0100, 0xE01EF},
|
||||
}
|
||||
|
||||
type ctype int
|
||||
|
||||
const (
|
||||
narrow ctype = iota
|
||||
ambiguous
|
||||
wide
|
||||
halfwidth
|
||||
fullwidth
|
||||
neutral
|
||||
)
|
||||
|
||||
type intervalType struct {
|
||||
first rune
|
||||
last rune
|
||||
ctype ctype
|
||||
}
|
||||
|
||||
var ctypes = []intervalType{
|
||||
{0x0020, 0x007E, narrow},
|
||||
{0x00A1, 0x00A1, ambiguous},
|
||||
{0x00A2, 0x00A3, narrow},
|
||||
{0x00A4, 0x00A4, ambiguous},
|
||||
{0x00A5, 0x00A6, narrow},
|
||||
{0x00A7, 0x00A8, ambiguous},
|
||||
{0x00AA, 0x00AA, ambiguous},
|
||||
{0x00AC, 0x00AC, narrow},
|
||||
{0x00AD, 0x00AE, ambiguous},
|
||||
{0x00AF, 0x00AF, narrow},
|
||||
{0x00B0, 0x00B4, ambiguous},
|
||||
{0x00B6, 0x00BA, ambiguous},
|
||||
{0x00BC, 0x00BF, ambiguous},
|
||||
{0x00C6, 0x00C6, ambiguous},
|
||||
{0x00D0, 0x00D0, ambiguous},
|
||||
{0x00D7, 0x00D8, ambiguous},
|
||||
{0x00DE, 0x00E1, ambiguous},
|
||||
{0x00E6, 0x00E6, ambiguous},
|
||||
{0x00E8, 0x00EA, ambiguous},
|
||||
{0x00EC, 0x00ED, ambiguous},
|
||||
{0x00F0, 0x00F0, ambiguous},
|
||||
{0x00F2, 0x00F3, ambiguous},
|
||||
{0x00F7, 0x00FA, ambiguous},
|
||||
{0x00FC, 0x00FC, ambiguous},
|
||||
{0x00FE, 0x00FE, ambiguous},
|
||||
{0x0101, 0x0101, ambiguous},
|
||||
{0x0111, 0x0111, ambiguous},
|
||||
{0x0113, 0x0113, ambiguous},
|
||||
{0x011B, 0x011B, ambiguous},
|
||||
{0x0126, 0x0127, ambiguous},
|
||||
{0x012B, 0x012B, ambiguous},
|
||||
{0x0131, 0x0133, ambiguous},
|
||||
{0x0138, 0x0138, ambiguous},
|
||||
{0x013F, 0x0142, ambiguous},
|
||||
{0x0144, 0x0144, ambiguous},
|
||||
{0x0148, 0x014B, ambiguous},
|
||||
{0x014D, 0x014D, ambiguous},
|
||||
{0x0152, 0x0153, ambiguous},
|
||||
{0x0166, 0x0167, ambiguous},
|
||||
{0x016B, 0x016B, ambiguous},
|
||||
{0x01CE, 0x01CE, ambiguous},
|
||||
{0x01D0, 0x01D0, ambiguous},
|
||||
{0x01D2, 0x01D2, ambiguous},
|
||||
{0x01D4, 0x01D4, ambiguous},
|
||||
{0x01D6, 0x01D6, ambiguous},
|
||||
{0x01D8, 0x01D8, ambiguous},
|
||||
{0x01DA, 0x01DA, ambiguous},
|
||||
{0x01DC, 0x01DC, ambiguous},
|
||||
{0x0251, 0x0251, ambiguous},
|
||||
{0x0261, 0x0261, ambiguous},
|
||||
{0x02C4, 0x02C4, ambiguous},
|
||||
{0x02C7, 0x02C7, ambiguous},
|
||||
{0x02C9, 0x02CB, ambiguous},
|
||||
{0x02CD, 0x02CD, ambiguous},
|
||||
{0x02D0, 0x02D0, ambiguous},
|
||||
{0x02D8, 0x02DB, ambiguous},
|
||||
{0x02DD, 0x02DD, ambiguous},
|
||||
{0x02DF, 0x02DF, ambiguous},
|
||||
{0x0300, 0x036F, ambiguous},
|
||||
{0x0391, 0x03A2, ambiguous},
|
||||
{0x03A3, 0x03A9, ambiguous},
|
||||
{0x03B1, 0x03C1, ambiguous},
|
||||
{0x03C3, 0x03C9, ambiguous},
|
||||
{0x0401, 0x0401, ambiguous},
|
||||
{0x0410, 0x044F, ambiguous},
|
||||
{0x0451, 0x0451, ambiguous},
|
||||
{0x1100, 0x115F, wide},
|
||||
{0x2010, 0x2010, ambiguous},
|
||||
{0x2013, 0x2016, ambiguous},
|
||||
{0x2018, 0x2019, ambiguous},
|
||||
{0x201C, 0x201D, ambiguous},
|
||||
{0x2020, 0x2022, ambiguous},
|
||||
{0x2024, 0x2027, ambiguous},
|
||||
{0x2030, 0x2030, ambiguous},
|
||||
{0x2032, 0x2033, ambiguous},
|
||||
{0x2035, 0x2035, ambiguous},
|
||||
{0x203B, 0x203B, ambiguous},
|
||||
{0x203E, 0x203E, ambiguous},
|
||||
{0x2074, 0x2074, ambiguous},
|
||||
{0x207F, 0x207F, ambiguous},
|
||||
{0x2081, 0x2084, ambiguous},
|
||||
{0x20A9, 0x20A9, halfwidth},
|
||||
{0x20AC, 0x20AC, ambiguous},
|
||||
{0x2103, 0x2103, ambiguous},
|
||||
{0x2105, 0x2105, ambiguous},
|
||||
{0x2109, 0x2109, ambiguous},
|
||||
{0x2113, 0x2113, ambiguous},
|
||||
{0x2116, 0x2116, ambiguous},
|
||||
{0x2121, 0x2122, ambiguous},
|
||||
{0x2126, 0x2126, ambiguous},
|
||||
{0x212B, 0x212B, ambiguous},
|
||||
{0x2153, 0x2154, ambiguous},
|
||||
{0x215B, 0x215E, ambiguous},
|
||||
{0x2160, 0x216B, ambiguous},
|
||||
{0x2170, 0x2179, ambiguous},
|
||||
{0x2189, 0x218A, ambiguous},
|
||||
{0x2190, 0x2199, ambiguous},
|
||||
{0x21B8, 0x21B9, ambiguous},
|
||||
{0x21D2, 0x21D2, ambiguous},
|
||||
{0x21D4, 0x21D4, ambiguous},
|
||||
{0x21E7, 0x21E7, ambiguous},
|
||||
{0x2200, 0x2200, ambiguous},
|
||||
{0x2202, 0x2203, ambiguous},
|
||||
{0x2207, 0x2208, ambiguous},
|
||||
{0x220B, 0x220B, ambiguous},
|
||||
{0x220F, 0x220F, ambiguous},
|
||||
{0x2211, 0x2211, ambiguous},
|
||||
{0x2215, 0x2215, ambiguous},
|
||||
{0x221A, 0x221A, ambiguous},
|
||||
{0x221D, 0x2220, ambiguous},
|
||||
{0x2223, 0x2223, ambiguous},
|
||||
{0x2225, 0x2225, ambiguous},
|
||||
{0x2227, 0x222C, ambiguous},
|
||||
{0x222E, 0x222E, ambiguous},
|
||||
{0x2234, 0x2237, ambiguous},
|
||||
{0x223C, 0x223D, ambiguous},
|
||||
{0x2248, 0x2248, ambiguous},
|
||||
{0x224C, 0x224C, ambiguous},
|
||||
{0x2252, 0x2252, ambiguous},
|
||||
{0x2260, 0x2261, ambiguous},
|
||||
{0x2264, 0x2267, ambiguous},
|
||||
{0x226A, 0x226B, ambiguous},
|
||||
{0x226E, 0x226F, ambiguous},
|
||||
{0x2282, 0x2283, ambiguous},
|
||||
{0x2286, 0x2287, ambiguous},
|
||||
{0x2295, 0x2295, ambiguous},
|
||||
{0x2299, 0x2299, ambiguous},
|
||||
{0x22A5, 0x22A5, ambiguous},
|
||||
{0x22BF, 0x22BF, ambiguous},
|
||||
{0x2312, 0x2312, ambiguous},
|
||||
{0x2329, 0x232A, wide},
|
||||
{0x2460, 0x24E9, ambiguous},
|
||||
{0x24EB, 0x254B, ambiguous},
|
||||
{0x2550, 0x2573, ambiguous},
|
||||
{0x2580, 0x258F, ambiguous},
|
||||
{0x2592, 0x2595, ambiguous},
|
||||
{0x25A0, 0x25A1, ambiguous},
|
||||
{0x25A3, 0x25A9, ambiguous},
|
||||
{0x25B2, 0x25B3, ambiguous},
|
||||
{0x25B6, 0x25B7, ambiguous},
|
||||
{0x25BC, 0x25BD, ambiguous},
|
||||
{0x25C0, 0x25C1, ambiguous},
|
||||
{0x25C6, 0x25C8, ambiguous},
|
||||
{0x25CB, 0x25CB, ambiguous},
|
||||
{0x25CE, 0x25D1, ambiguous},
|
||||
{0x25E2, 0x25E5, ambiguous},
|
||||
{0x25EF, 0x25EF, ambiguous},
|
||||
{0x2605, 0x2606, ambiguous},
|
||||
{0x2609, 0x2609, ambiguous},
|
||||
{0x260E, 0x260F, ambiguous},
|
||||
{0x2614, 0x2615, ambiguous},
|
||||
{0x261C, 0x261C, ambiguous},
|
||||
{0x261E, 0x261E, ambiguous},
|
||||
{0x2640, 0x2640, ambiguous},
|
||||
{0x2642, 0x2642, ambiguous},
|
||||
{0x2660, 0x2661, ambiguous},
|
||||
{0x2663, 0x2665, ambiguous},
|
||||
{0x2667, 0x266A, ambiguous},
|
||||
{0x266C, 0x266D, ambiguous},
|
||||
{0x266F, 0x266F, ambiguous},
|
||||
{0x269E, 0x269F, ambiguous},
|
||||
{0x26BE, 0x26BF, ambiguous},
|
||||
{0x26C4, 0x26CD, ambiguous},
|
||||
{0x26CF, 0x26E1, ambiguous},
|
||||
{0x26E3, 0x26E3, ambiguous},
|
||||
{0x26E8, 0x26FF, ambiguous},
|
||||
{0x273D, 0x273D, ambiguous},
|
||||
{0x2757, 0x2757, ambiguous},
|
||||
{0x2776, 0x277F, ambiguous},
|
||||
{0x27E6, 0x27ED, narrow},
|
||||
{0x2985, 0x2986, narrow},
|
||||
{0x2B55, 0x2B59, ambiguous},
|
||||
{0x2E80, 0x2E9A, wide},
|
||||
{0x2E9B, 0x2EF4, wide},
|
||||
{0x2F00, 0x2FD6, wide},
|
||||
{0x2FF0, 0x2FFC, wide},
|
||||
{0x3000, 0x3000, fullwidth},
|
||||
{0x3001, 0x303E, wide},
|
||||
{0x3041, 0x3097, wide},
|
||||
{0x3099, 0x3100, wide},
|
||||
{0x3105, 0x312E, wide},
|
||||
{0x3131, 0x318F, wide},
|
||||
{0x3190, 0x31BB, wide},
|
||||
{0x31C0, 0x31E4, wide},
|
||||
{0x31F0, 0x321F, wide},
|
||||
{0x3220, 0x3247, wide},
|
||||
{0x3248, 0x324F, ambiguous},
|
||||
{0x3250, 0x32FF, wide},
|
||||
{0x3300, 0x4DBF, wide},
|
||||
{0x4E00, 0xA48D, wide},
|
||||
{0xA490, 0xA4C7, wide},
|
||||
{0xA960, 0xA97D, wide},
|
||||
{0xAC00, 0xD7A4, wide},
|
||||
{0xE000, 0xF8FF, ambiguous},
|
||||
{0xF900, 0xFAFF, wide},
|
||||
{0xFE00, 0xFE0F, ambiguous},
|
||||
{0xFE10, 0xFE1A, wide},
|
||||
{0xFE30, 0xFE53, wide},
|
||||
{0xFE54, 0xFE67, wide},
|
||||
{0xFE68, 0xFE6C, wide},
|
||||
{0xFF01, 0xFF60, fullwidth},
|
||||
{0xFF61, 0xFFBF, halfwidth},
|
||||
{0xFFC2, 0xFFC8, halfwidth},
|
||||
{0xFFCA, 0xFFD0, halfwidth},
|
||||
{0xFFD2, 0xFFD8, halfwidth},
|
||||
{0xFFDA, 0xFFDD, halfwidth},
|
||||
{0xFFE0, 0xFFE7, fullwidth},
|
||||
{0xFFE8, 0xFFEF, halfwidth},
|
||||
{0xFFFD, 0xFFFE, ambiguous},
|
||||
{0x1B000, 0x1B002, wide},
|
||||
{0x1F100, 0x1F10A, ambiguous},
|
||||
{0x1F110, 0x1F12D, ambiguous},
|
||||
{0x1F130, 0x1F169, ambiguous},
|
||||
{0x1F170, 0x1F19B, ambiguous},
|
||||
{0x1F200, 0x1F203, wide},
|
||||
{0x1F210, 0x1F23B, wide},
|
||||
{0x1F240, 0x1F249, wide},
|
||||
{0x1F250, 0x1F252, wide},
|
||||
{0x20000, 0x2FFFE, wide},
|
||||
{0x30000, 0x3FFFE, wide},
|
||||
{0xE0100, 0xE01F0, ambiguous},
|
||||
{0xF0000, 0xFFFFD, ambiguous},
|
||||
{0x100000, 0x10FFFE, ambiguous},
|
||||
}
|
||||
|
||||
// Condition have flag EastAsianWidth whether the current locale is CJK or not.
|
||||
type Condition struct {
|
||||
EastAsianWidth bool
|
||||
}
|
||||
|
||||
// NewCondition return new instance of Condition which is current locale.
|
||||
func NewCondition() *Condition {
|
||||
return &Condition{EastAsianWidth}
|
||||
}
|
||||
|
||||
// RuneWidth returns the number of cells in r.
|
||||
// See http://www.unicode.org/reports/tr11/
|
||||
func (c *Condition) RuneWidth(r rune) int {
|
||||
if r == 0 {
|
||||
return 0
|
||||
}
|
||||
if r < 32 || (r >= 0x7f && r < 0xa0) {
|
||||
return 1
|
||||
}
|
||||
for _, iv := range combining {
|
||||
if iv.first <= r && r <= iv.last {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
if c.EastAsianWidth && IsAmbiguousWidth(r) {
|
||||
return 2
|
||||
}
|
||||
|
||||
if r >= 0x1100 &&
|
||||
(r <= 0x115f || r == 0x2329 || r == 0x232a ||
|
||||
(r >= 0x2e80 && r <= 0xa4cf && r != 0x303f) ||
|
||||
(r >= 0xac00 && r <= 0xd7a3) ||
|
||||
(r >= 0xf900 && r <= 0xfaff) ||
|
||||
(r >= 0xfe30 && r <= 0xfe6f) ||
|
||||
(r >= 0xff00 && r <= 0xff60) ||
|
||||
(r >= 0xffe0 && r <= 0xffe6) ||
|
||||
(r >= 0x20000 && r <= 0x2fffd) ||
|
||||
(r >= 0x30000 && r <= 0x3fffd)) {
|
||||
return 2
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
// StringWidth return width as you can see
|
||||
func (c *Condition) StringWidth(s string) (width int) {
|
||||
for _, r := range []rune(s) {
|
||||
width += c.RuneWidth(r)
|
||||
}
|
||||
return width
|
||||
}
|
||||
|
||||
// Truncate return string truncated with w cells
|
||||
func (c *Condition) Truncate(s string, w int, tail string) string {
|
||||
if c.StringWidth(s) <= w {
|
||||
return s
|
||||
}
|
||||
r := []rune(s)
|
||||
tw := c.StringWidth(tail)
|
||||
w -= tw
|
||||
width := 0
|
||||
i := 0
|
||||
for ; i < len(r); i++ {
|
||||
cw := c.RuneWidth(r[i])
|
||||
if width+cw > w {
|
||||
break
|
||||
}
|
||||
width += cw
|
||||
}
|
||||
return string(r[0:i]) + tail
|
||||
}
|
||||
|
||||
// Wrap return string wrapped with w cells
|
||||
func (c *Condition) Wrap(s string, w int) string {
|
||||
width := 0
|
||||
out := ""
|
||||
for _, r := range []rune(s) {
|
||||
cw := RuneWidth(r)
|
||||
if r == '\n' {
|
||||
out += string(r)
|
||||
width = 0
|
||||
continue
|
||||
} else if width+cw > w {
|
||||
out += "\n"
|
||||
width = 0
|
||||
out += string(r)
|
||||
width += cw
|
||||
continue
|
||||
}
|
||||
out += string(r)
|
||||
width += cw
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// FillLeft return string filled in left by spaces in w cells
|
||||
func (c *Condition) FillLeft(s string, w int) string {
|
||||
width := c.StringWidth(s)
|
||||
count := w - width
|
||||
if count > 0 {
|
||||
b := make([]byte, count)
|
||||
for i := range b {
|
||||
b[i] = ' '
|
||||
}
|
||||
return string(b) + s
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// FillRight return string filled in left by spaces in w cells
|
||||
func (c *Condition) FillRight(s string, w int) string {
|
||||
width := c.StringWidth(s)
|
||||
count := w - width
|
||||
if count > 0 {
|
||||
b := make([]byte, count)
|
||||
for i := range b {
|
||||
b[i] = ' '
|
||||
}
|
||||
return s + string(b)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// RuneWidth returns the number of cells in r.
|
||||
// See http://www.unicode.org/reports/tr11/
|
||||
func RuneWidth(r rune) int {
|
||||
return DefaultCondition.RuneWidth(r)
|
||||
}
|
||||
|
||||
func ct(r rune) ctype {
|
||||
for _, iv := range ctypes {
|
||||
if iv.first <= r && r <= iv.last {
|
||||
return iv.ctype
|
||||
}
|
||||
}
|
||||
return neutral
|
||||
}
|
||||
|
||||
// IsAmbiguousWidth returns whether is ambiguous width or not.
|
||||
func IsAmbiguousWidth(r rune) bool {
|
||||
return ct(r) == ambiguous
|
||||
}
|
||||
|
||||
// IsNeutralWidth returns whether is neutral width or not.
|
||||
func IsNeutralWidth(r rune) bool {
|
||||
return ct(r) == neutral
|
||||
}
|
||||
|
||||
// StringWidth return width as you can see
|
||||
func StringWidth(s string) (width int) {
|
||||
return DefaultCondition.StringWidth(s)
|
||||
}
|
||||
|
||||
// Truncate return string truncated with w cells
|
||||
func Truncate(s string, w int, tail string) string {
|
||||
return DefaultCondition.Truncate(s, w, tail)
|
||||
}
|
||||
|
||||
// Wrap return string wrapped with w cells
|
||||
func Wrap(s string, w int) string {
|
||||
return DefaultCondition.Wrap(s, w)
|
||||
}
|
||||
|
||||
// FillLeft return string filled in left by spaces in w cells
|
||||
func FillLeft(s string, w int) string {
|
||||
return DefaultCondition.FillLeft(s, w)
|
||||
}
|
||||
|
||||
// FillRight return string filled in left by spaces in w cells
|
||||
func FillRight(s string, w int) string {
|
||||
return DefaultCondition.FillRight(s, w)
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
// +build js
|
||||
|
||||
package runewidth
|
||||
|
||||
func IsEastAsian() bool {
|
||||
// TODO: Implement this for the web. Detect east asian in a compatible way, and return true.
|
||||
return false
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
// +build !windows,!js
|
||||
|
||||
package runewidth
|
||||
|
||||
import (
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var reLoc = regexp.MustCompile(`^[a-z][a-z][a-z]?(?:_[A-Z][A-Z])?\.(.+)`)
|
||||
|
||||
var mblenTable = map[string]int{
|
||||
"utf-8": 6,
|
||||
"utf8": 6,
|
||||
"jis": 8,
|
||||
"eucjp": 3,
|
||||
"euckr": 2,
|
||||
"euccn": 2,
|
||||
"sjis": 2,
|
||||
"cp932": 2,
|
||||
"cp51932": 2,
|
||||
"cp936": 2,
|
||||
"cp949": 2,
|
||||
"cp950": 2,
|
||||
"big5": 2,
|
||||
"gbk": 2,
|
||||
"gb2312": 2,
|
||||
}
|
||||
|
||||
func isEastAsian(locale string) bool {
|
||||
charset := strings.ToLower(locale)
|
||||
r := reLoc.FindStringSubmatch(locale)
|
||||
if len(r) == 2 {
|
||||
charset = strings.ToLower(r[1])
|
||||
}
|
||||
|
||||
if strings.HasSuffix(charset, "@cjk_narrow") {
|
||||
return false
|
||||
}
|
||||
|
||||
for pos, b := range []byte(charset) {
|
||||
if b == '@' {
|
||||
charset = charset[:pos]
|
||||
break
|
||||
}
|
||||
}
|
||||
max := 1
|
||||
if m, ok := mblenTable[charset]; ok {
|
||||
max = m
|
||||
}
|
||||
if max > 1 && (charset[0] != 'u' ||
|
||||
strings.HasPrefix(locale, "ja") ||
|
||||
strings.HasPrefix(locale, "ko") ||
|
||||
strings.HasPrefix(locale, "zh")) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsEastAsian return true if the current locale is CJK
|
||||
func IsEastAsian() bool {
|
||||
locale := os.Getenv("LC_CTYPE")
|
||||
if locale == "" {
|
||||
locale = os.Getenv("LANG")
|
||||
}
|
||||
|
||||
// ignore C locale
|
||||
if locale == "POSIX" || locale == "C" {
|
||||
return false
|
||||
}
|
||||
if len(locale) > 1 && locale[0] == 'C' && (locale[1] == '.' || locale[1] == '-') {
|
||||
return false
|
||||
}
|
||||
|
||||
return isEastAsian(locale)
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
package runewidth
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
var (
|
||||
kernel32 = syscall.NewLazyDLL("kernel32")
|
||||
procGetConsoleOutputCP = kernel32.NewProc("GetConsoleOutputCP")
|
||||
)
|
||||
|
||||
// IsEastAsian return true if the current locale is CJK
|
||||
func IsEastAsian() bool {
|
||||
r1, _, _ := procGetConsoleOutputCP.Call()
|
||||
if r1 == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
switch int(r1) {
|
||||
case 932, 51932, 936, 949, 950:
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
// Copyright 2014 Oleku Konko All rights reserved.
|
||||
// Use of this source code is governed by a MIT
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// This module is a Table Writer API for the Go Programming Language.
|
||||
// The protocols were written in pure Go and works on windows and unix systems
|
||||
|
||||
package tablewriter
|
||||
|
||||
import (
|
||||
"encoding/csv"
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
// Start A new table by importing from a CSV file
|
||||
// Takes io.Writer and csv File name
|
||||
func NewCSV(writer io.Writer, fileName string, hasHeader bool) (*Table, error) {
|
||||
file, err := os.Open(fileName)
|
||||
if err != nil {
|
||||
return &Table{}, err
|
||||
}
|
||||
defer file.Close()
|
||||
csvReader := csv.NewReader(file)
|
||||
t, err := NewCSVReader(writer, csvReader, hasHeader)
|
||||
return t, err
|
||||
}
|
||||
|
||||
// Start a New Table Writer with csv.Reader
|
||||
// This enables customisation such as reader.Comma = ';'
|
||||
// See http://golang.org/src/pkg/encoding/csv/reader.go?s=3213:3671#L94
|
||||
func NewCSVReader(writer io.Writer, csvReader *csv.Reader, hasHeader bool) (*Table, error) {
|
||||
t := NewWriter(writer)
|
||||
if hasHeader {
|
||||
// Read the first row
|
||||
headers, err := csvReader.Read()
|
||||
if err != nil {
|
||||
return &Table{}, err
|
||||
}
|
||||
t.SetHeader(headers)
|
||||
}
|
||||
for {
|
||||
record, err := csvReader.Read()
|
||||
if err == io.EOF {
|
||||
break
|
||||
} else if err != nil {
|
||||
return &Table{}, err
|
||||
}
|
||||
t.Append(record)
|
||||
}
|
||||
return t, nil
|
||||
}
|
|
@ -0,0 +1,662 @@
|
|||
// Copyright 2014 Oleku Konko All rights reserved.
|
||||
// Use of this source code is governed by a MIT
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// This module is a Table Writer API for the Go Programming Language.
|
||||
// The protocols were written in pure Go and works on windows and unix systems
|
||||
|
||||
// Create & Generate text based table
|
||||
package tablewriter
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
MAX_ROW_WIDTH = 30
|
||||
)
|
||||
|
||||
const (
|
||||
CENTER = "+"
|
||||
ROW = "-"
|
||||
COLUMN = "|"
|
||||
SPACE = " "
|
||||
NEWLINE = "\n"
|
||||
)
|
||||
|
||||
const (
|
||||
ALIGN_DEFAULT = iota
|
||||
ALIGN_CENTER
|
||||
ALIGN_RIGHT
|
||||
ALIGN_LEFT
|
||||
)
|
||||
|
||||
var (
|
||||
decimal = regexp.MustCompile(`^-*\d*\.?\d*$`)
|
||||
percent = regexp.MustCompile(`^-*\d*\.?\d*$%$`)
|
||||
)
|
||||
|
||||
type Border struct {
|
||||
Left bool
|
||||
Right bool
|
||||
Top bool
|
||||
Bottom bool
|
||||
}
|
||||
|
||||
type Table struct {
|
||||
out io.Writer
|
||||
rows [][]string
|
||||
lines [][][]string
|
||||
cs map[int]int
|
||||
rs map[int]int
|
||||
headers []string
|
||||
footers []string
|
||||
autoFmt bool
|
||||
autoWrap bool
|
||||
mW int
|
||||
pCenter string
|
||||
pRow string
|
||||
pColumn string
|
||||
tColumn int
|
||||
tRow int
|
||||
hAlign int
|
||||
fAlign int
|
||||
align int
|
||||
newLine string
|
||||
rowLine bool
|
||||
autoMergeCells bool
|
||||
hdrLine bool
|
||||
borders Border
|
||||
colSize int
|
||||
}
|
||||
|
||||
// Start New Table
|
||||
// Take io.Writer Directly
|
||||
func NewWriter(writer io.Writer) *Table {
|
||||
t := &Table{
|
||||
out: writer,
|
||||
rows: [][]string{},
|
||||
lines: [][][]string{},
|
||||
cs: make(map[int]int),
|
||||
rs: make(map[int]int),
|
||||
headers: []string{},
|
||||
footers: []string{},
|
||||
autoFmt: true,
|
||||
autoWrap: true,
|
||||
mW: MAX_ROW_WIDTH,
|
||||
pCenter: CENTER,
|
||||
pRow: ROW,
|
||||
pColumn: COLUMN,
|
||||
tColumn: -1,
|
||||
tRow: -1,
|
||||
hAlign: ALIGN_DEFAULT,
|
||||
fAlign: ALIGN_DEFAULT,
|
||||
align: ALIGN_DEFAULT,
|
||||
newLine: NEWLINE,
|
||||
rowLine: false,
|
||||
hdrLine: true,
|
||||
borders: Border{Left: true, Right: true, Bottom: true, Top: true},
|
||||
colSize: -1}
|
||||
return t
|
||||
}
|
||||
|
||||
// Render table output
|
||||
func (t Table) Render() {
|
||||
if t.borders.Top {
|
||||
t.printLine(true)
|
||||
}
|
||||
t.printHeading()
|
||||
if t.autoMergeCells {
|
||||
t.printRowsMergeCells()
|
||||
} else {
|
||||
t.printRows()
|
||||
}
|
||||
|
||||
if !t.rowLine && t.borders.Bottom {
|
||||
t.printLine(true)
|
||||
}
|
||||
t.printFooter()
|
||||
|
||||
}
|
||||
|
||||
// Set table header
|
||||
func (t *Table) SetHeader(keys []string) {
|
||||
t.colSize = len(keys)
|
||||
for i, v := range keys {
|
||||
t.parseDimension(v, i, -1)
|
||||
t.headers = append(t.headers, v)
|
||||
}
|
||||
}
|
||||
|
||||
// Set table Footer
|
||||
func (t *Table) SetFooter(keys []string) {
|
||||
//t.colSize = len(keys)
|
||||
for i, v := range keys {
|
||||
t.parseDimension(v, i, -1)
|
||||
t.footers = append(t.footers, v)
|
||||
}
|
||||
}
|
||||
|
||||
// Turn header autoformatting on/off. Default is on (true).
|
||||
func (t *Table) SetAutoFormatHeaders(auto bool) {
|
||||
t.autoFmt = auto
|
||||
}
|
||||
|
||||
// Turn automatic multiline text adjustment on/off. Default is on (true).
|
||||
func (t *Table) SetAutoWrapText(auto bool) {
|
||||
t.autoWrap = auto
|
||||
}
|
||||
|
||||
// Set the Default column width
|
||||
func (t *Table) SetColWidth(width int) {
|
||||
t.mW = width
|
||||
}
|
||||
|
||||
// Set the Column Separator
|
||||
func (t *Table) SetColumnSeparator(sep string) {
|
||||
t.pColumn = sep
|
||||
}
|
||||
|
||||
// Set the Row Separator
|
||||
func (t *Table) SetRowSeparator(sep string) {
|
||||
t.pRow = sep
|
||||
}
|
||||
|
||||
// Set the center Separator
|
||||
func (t *Table) SetCenterSeparator(sep string) {
|
||||
t.pCenter = sep
|
||||
}
|
||||
|
||||
// Set Header Alignment
|
||||
func (t *Table) SetHeaderAlignment(hAlign int) {
|
||||
t.hAlign = hAlign
|
||||
}
|
||||
|
||||
// Set Footer Alignment
|
||||
func (t *Table) SetFooterAlignment(fAlign int) {
|
||||
t.fAlign = fAlign
|
||||
}
|
||||
|
||||
// Set Table Alignment
|
||||
func (t *Table) SetAlignment(align int) {
|
||||
t.align = align
|
||||
}
|
||||
|
||||
// Set New Line
|
||||
func (t *Table) SetNewLine(nl string) {
|
||||
t.newLine = nl
|
||||
}
|
||||
|
||||
// Set Header Line
|
||||
// This would enable / disable a line after the header
|
||||
func (t *Table) SetHeaderLine(line bool) {
|
||||
t.hdrLine = line
|
||||
}
|
||||
|
||||
// Set Row Line
|
||||
// This would enable / disable a line on each row of the table
|
||||
func (t *Table) SetRowLine(line bool) {
|
||||
t.rowLine = line
|
||||
}
|
||||
|
||||
// Set Auto Merge Cells
|
||||
// This would enable / disable the merge of cells with identical values
|
||||
func (t *Table) SetAutoMergeCells(auto bool) {
|
||||
t.autoMergeCells = auto
|
||||
}
|
||||
|
||||
// Set Table Border
|
||||
// This would enable / disable line around the table
|
||||
func (t *Table) SetBorder(border bool) {
|
||||
t.SetBorders(Border{border, border, border, border})
|
||||
}
|
||||
|
||||
func (t *Table) SetBorders(border Border) {
|
||||
t.borders = border
|
||||
}
|
||||
|
||||
// Append row to table
|
||||
func (t *Table) Append(row []string) {
|
||||
rowSize := len(t.headers)
|
||||
if rowSize > t.colSize {
|
||||
t.colSize = rowSize
|
||||
}
|
||||
|
||||
n := len(t.lines)
|
||||
line := [][]string{}
|
||||
for i, v := range row {
|
||||
|
||||
// Detect string width
|
||||
// Detect String height
|
||||
// Break strings into words
|
||||
out := t.parseDimension(v, i, n)
|
||||
|
||||
// Append broken words
|
||||
line = append(line, out)
|
||||
}
|
||||
t.lines = append(t.lines, line)
|
||||
}
|
||||
|
||||
// Allow Support for Bulk Append
|
||||
// Eliminates repeated for loops
|
||||
func (t *Table) AppendBulk(rows [][]string) {
|
||||
for _, row := range rows {
|
||||
t.Append(row)
|
||||
}
|
||||
}
|
||||
|
||||
// Print line based on row width
|
||||
func (t Table) printLine(nl bool) {
|
||||
fmt.Fprint(t.out, t.pCenter)
|
||||
for i := 0; i < len(t.cs); i++ {
|
||||
v := t.cs[i]
|
||||
fmt.Fprintf(t.out, "%s%s%s%s",
|
||||
t.pRow,
|
||||
strings.Repeat(string(t.pRow), v),
|
||||
t.pRow,
|
||||
t.pCenter)
|
||||
}
|
||||
if nl {
|
||||
fmt.Fprint(t.out, t.newLine)
|
||||
}
|
||||
}
|
||||
|
||||
// Print line based on row width with our without cell separator
|
||||
func (t Table) printLineOptionalCellSeparators(nl bool, displayCellSeparator []bool) {
|
||||
fmt.Fprint(t.out, t.pCenter)
|
||||
for i := 0; i < len(t.cs); i++ {
|
||||
v := t.cs[i]
|
||||
if i > len(displayCellSeparator) || displayCellSeparator[i] {
|
||||
// Display the cell separator
|
||||
fmt.Fprintf(t.out, "%s%s%s%s",
|
||||
t.pRow,
|
||||
strings.Repeat(string(t.pRow), v),
|
||||
t.pRow,
|
||||
t.pCenter)
|
||||
} else {
|
||||
// Don't display the cell separator for this cell
|
||||
fmt.Fprintf(t.out, "%s%s",
|
||||
strings.Repeat(" ", v+2),
|
||||
t.pCenter)
|
||||
}
|
||||
}
|
||||
if nl {
|
||||
fmt.Fprint(t.out, t.newLine)
|
||||
}
|
||||
}
|
||||
|
||||
// Return the PadRight function if align is left, PadLeft if align is right,
|
||||
// and Pad by default
|
||||
func pad(align int) func(string, string, int) string {
|
||||
padFunc := Pad
|
||||
switch align {
|
||||
case ALIGN_LEFT:
|
||||
padFunc = PadRight
|
||||
case ALIGN_RIGHT:
|
||||
padFunc = PadLeft
|
||||
}
|
||||
return padFunc
|
||||
}
|
||||
|
||||
// Print heading information
|
||||
func (t Table) printHeading() {
|
||||
// Check if headers is available
|
||||
if len(t.headers) < 1 {
|
||||
return
|
||||
}
|
||||
|
||||
// Check if border is set
|
||||
// Replace with space if not set
|
||||
fmt.Fprint(t.out, ConditionString(t.borders.Left, t.pColumn, SPACE))
|
||||
|
||||
// Identify last column
|
||||
end := len(t.cs) - 1
|
||||
|
||||
// Get pad function
|
||||
padFunc := pad(t.hAlign)
|
||||
|
||||
// Print Heading column
|
||||
for i := 0; i <= end; i++ {
|
||||
v := t.cs[i]
|
||||
h := t.headers[i]
|
||||
if t.autoFmt {
|
||||
h = Title(h)
|
||||
}
|
||||
pad := ConditionString((i == end && !t.borders.Left), SPACE, t.pColumn)
|
||||
fmt.Fprintf(t.out, " %s %s",
|
||||
padFunc(h, SPACE, v),
|
||||
pad)
|
||||
}
|
||||
// Next line
|
||||
fmt.Fprint(t.out, t.newLine)
|
||||
if t.hdrLine {
|
||||
t.printLine(true)
|
||||
}
|
||||
}
|
||||
|
||||
// Print heading information
|
||||
func (t Table) printFooter() {
|
||||
// Check if headers is available
|
||||
if len(t.footers) < 1 {
|
||||
return
|
||||
}
|
||||
|
||||
// Only print line if border is not set
|
||||
if !t.borders.Bottom {
|
||||
t.printLine(true)
|
||||
}
|
||||
// Check if border is set
|
||||
// Replace with space if not set
|
||||
fmt.Fprint(t.out, ConditionString(t.borders.Bottom, t.pColumn, SPACE))
|
||||
|
||||
// Identify last column
|
||||
end := len(t.cs) - 1
|
||||
|
||||
// Get pad function
|
||||
padFunc := pad(t.fAlign)
|
||||
|
||||
// Print Heading column
|
||||
for i := 0; i <= end; i++ {
|
||||
v := t.cs[i]
|
||||
f := t.footers[i]
|
||||
if t.autoFmt {
|
||||
f = Title(f)
|
||||
}
|
||||
pad := ConditionString((i == end && !t.borders.Top), SPACE, t.pColumn)
|
||||
|
||||
if len(t.footers[i]) == 0 {
|
||||
pad = SPACE
|
||||
}
|
||||
fmt.Fprintf(t.out, " %s %s",
|
||||
padFunc(f, SPACE, v),
|
||||
pad)
|
||||
}
|
||||
// Next line
|
||||
fmt.Fprint(t.out, t.newLine)
|
||||
//t.printLine(true)
|
||||
|
||||
hasPrinted := false
|
||||
|
||||
for i := 0; i <= end; i++ {
|
||||
v := t.cs[i]
|
||||
pad := t.pRow
|
||||
center := t.pCenter
|
||||
length := len(t.footers[i])
|
||||
|
||||
if length > 0 {
|
||||
hasPrinted = true
|
||||
}
|
||||
|
||||
// Set center to be space if length is 0
|
||||
if length == 0 && !t.borders.Right {
|
||||
center = SPACE
|
||||
}
|
||||
|
||||
// Print first junction
|
||||
if i == 0 {
|
||||
fmt.Fprint(t.out, center)
|
||||
}
|
||||
|
||||
// Pad With space of length is 0
|
||||
if length == 0 {
|
||||
pad = SPACE
|
||||
}
|
||||
// Ignore left space of it has printed before
|
||||
if hasPrinted || t.borders.Left {
|
||||
pad = t.pRow
|
||||
center = t.pCenter
|
||||
}
|
||||
|
||||
// Change Center start position
|
||||
if center == SPACE {
|
||||
if i < end && len(t.footers[i+1]) != 0 {
|
||||
center = t.pCenter
|
||||
}
|
||||
}
|
||||
|
||||
// Print the footer
|
||||
fmt.Fprintf(t.out, "%s%s%s%s",
|
||||
pad,
|
||||
strings.Repeat(string(pad), v),
|
||||
pad,
|
||||
center)
|
||||
|
||||
}
|
||||
|
||||
fmt.Fprint(t.out, t.newLine)
|
||||
|
||||
}
|
||||
|
||||
func (t Table) printRows() {
|
||||
for i, lines := range t.lines {
|
||||
t.printRow(lines, i)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Print Row Information
|
||||
// Adjust column alignment based on type
|
||||
|
||||
func (t Table) printRow(columns [][]string, colKey int) {
|
||||
// Get Maximum Height
|
||||
max := t.rs[colKey]
|
||||
total := len(columns)
|
||||
|
||||
// TODO Fix uneven col size
|
||||
// if total < t.colSize {
|
||||
// for n := t.colSize - total; n < t.colSize ; n++ {
|
||||
// columns = append(columns, []string{SPACE})
|
||||
// t.cs[n] = t.mW
|
||||
// }
|
||||
//}
|
||||
|
||||
// Pad Each Height
|
||||
// pads := []int{}
|
||||
pads := []int{}
|
||||
|
||||
for i, line := range columns {
|
||||
length := len(line)
|
||||
pad := max - length
|
||||
pads = append(pads, pad)
|
||||
for n := 0; n < pad; n++ {
|
||||
columns[i] = append(columns[i], " ")
|
||||
}
|
||||
}
|
||||
//fmt.Println(max, "\n")
|
||||
for x := 0; x < max; x++ {
|
||||
for y := 0; y < total; y++ {
|
||||
|
||||
// Check if border is set
|
||||
fmt.Fprint(t.out, ConditionString((!t.borders.Left && y == 0), SPACE, t.pColumn))
|
||||
|
||||
fmt.Fprintf(t.out, SPACE)
|
||||
str := columns[y][x]
|
||||
|
||||
// This would print alignment
|
||||
// Default alignment would use multiple configuration
|
||||
switch t.align {
|
||||
case ALIGN_CENTER: //
|
||||
fmt.Fprintf(t.out, "%s", Pad(str, SPACE, t.cs[y]))
|
||||
case ALIGN_RIGHT:
|
||||
fmt.Fprintf(t.out, "%s", PadLeft(str, SPACE, t.cs[y]))
|
||||
case ALIGN_LEFT:
|
||||
fmt.Fprintf(t.out, "%s", PadRight(str, SPACE, t.cs[y]))
|
||||
default:
|
||||
if decimal.MatchString(strings.TrimSpace(str)) || percent.MatchString(strings.TrimSpace(str)) {
|
||||
fmt.Fprintf(t.out, "%s", PadLeft(str, SPACE, t.cs[y]))
|
||||
} else {
|
||||
fmt.Fprintf(t.out, "%s", PadRight(str, SPACE, t.cs[y]))
|
||||
|
||||
// TODO Custom alignment per column
|
||||
//if max == 1 || pads[y] > 0 {
|
||||
// fmt.Fprintf(t.out, "%s", Pad(str, SPACE, t.cs[y]))
|
||||
//} else {
|
||||
// fmt.Fprintf(t.out, "%s", PadRight(str, SPACE, t.cs[y]))
|
||||
//}
|
||||
|
||||
}
|
||||
}
|
||||
fmt.Fprintf(t.out, SPACE)
|
||||
}
|
||||
// Check if border is set
|
||||
// Replace with space if not set
|
||||
fmt.Fprint(t.out, ConditionString(t.borders.Left, t.pColumn, SPACE))
|
||||
fmt.Fprint(t.out, t.newLine)
|
||||
}
|
||||
|
||||
if t.rowLine {
|
||||
t.printLine(true)
|
||||
}
|
||||
}
|
||||
|
||||
// Print the rows of the table and merge the cells that are identical
|
||||
func (t Table) printRowsMergeCells() {
|
||||
var previousLine []string
|
||||
var displayCellBorder []bool
|
||||
var tmpWriter bytes.Buffer
|
||||
for i, lines := range t.lines {
|
||||
// We store the display of the current line in a tmp writer, as we need to know which border needs to be print above
|
||||
previousLine, displayCellBorder = t.printRowMergeCells(&tmpWriter, lines, i, previousLine)
|
||||
if i > 0 { //We don't need to print borders above first line
|
||||
if t.rowLine {
|
||||
t.printLineOptionalCellSeparators(true, displayCellBorder)
|
||||
}
|
||||
}
|
||||
tmpWriter.WriteTo(t.out)
|
||||
}
|
||||
//Print the end of the table
|
||||
if t.rowLine {
|
||||
t.printLine(true)
|
||||
}
|
||||
}
|
||||
|
||||
// Print Row Information to a writer and merge identical cells.
|
||||
// Adjust column alignment based on type
|
||||
|
||||
func (t Table) printRowMergeCells(writer io.Writer, columns [][]string, colKey int, previousLine []string) ([]string, []bool) {
|
||||
// Get Maximum Height
|
||||
max := t.rs[colKey]
|
||||
total := len(columns)
|
||||
|
||||
// Pad Each Height
|
||||
pads := []int{}
|
||||
|
||||
for i, line := range columns {
|
||||
length := len(line)
|
||||
pad := max - length
|
||||
pads = append(pads, pad)
|
||||
for n := 0; n < pad; n++ {
|
||||
columns[i] = append(columns[i], " ")
|
||||
}
|
||||
}
|
||||
|
||||
var displayCellBorder []bool
|
||||
for x := 0; x < max; x++ {
|
||||
for y := 0; y < total; y++ {
|
||||
|
||||
// Check if border is set
|
||||
fmt.Fprint(writer, ConditionString((!t.borders.Left && y == 0), SPACE, t.pColumn))
|
||||
|
||||
fmt.Fprintf(writer, SPACE)
|
||||
|
||||
str := columns[y][x]
|
||||
|
||||
if t.autoMergeCells {
|
||||
//Store the full line to merge mutli-lines cells
|
||||
fullLine := strings.Join(columns[y], " ")
|
||||
if len(previousLine) > y && fullLine == previousLine[y] && fullLine != "" {
|
||||
// If this cell is identical to the one above but not empty, we don't display the border and keep the cell empty.
|
||||
displayCellBorder = append(displayCellBorder, false)
|
||||
str = ""
|
||||
} else {
|
||||
// First line or different content, keep the content and print the cell border
|
||||
displayCellBorder = append(displayCellBorder, true)
|
||||
}
|
||||
}
|
||||
|
||||
// This would print alignment
|
||||
// Default alignment would use multiple configuration
|
||||
switch t.align {
|
||||
case ALIGN_CENTER: //
|
||||
fmt.Fprintf(writer, "%s", Pad(str, SPACE, t.cs[y]))
|
||||
case ALIGN_RIGHT:
|
||||
fmt.Fprintf(writer, "%s", PadLeft(str, SPACE, t.cs[y]))
|
||||
case ALIGN_LEFT:
|
||||
fmt.Fprintf(writer, "%s", PadRight(str, SPACE, t.cs[y]))
|
||||
default:
|
||||
if decimal.MatchString(strings.TrimSpace(str)) || percent.MatchString(strings.TrimSpace(str)) {
|
||||
fmt.Fprintf(writer, "%s", PadLeft(str, SPACE, t.cs[y]))
|
||||
} else {
|
||||
fmt.Fprintf(writer, "%s", PadRight(str, SPACE, t.cs[y]))
|
||||
}
|
||||
}
|
||||
fmt.Fprintf(writer, SPACE)
|
||||
}
|
||||
// Check if border is set
|
||||
// Replace with space if not set
|
||||
fmt.Fprint(writer, ConditionString(t.borders.Left, t.pColumn, SPACE))
|
||||
fmt.Fprint(writer, t.newLine)
|
||||
}
|
||||
|
||||
//The new previous line is the current one
|
||||
previousLine = make([]string, total)
|
||||
for y := 0; y < total; y++ {
|
||||
previousLine[y] = strings.Join(columns[y], " ") //Store the full line for multi-lines cells
|
||||
}
|
||||
//Returns the newly added line and wether or not a border should be displayed above.
|
||||
return previousLine, displayCellBorder
|
||||
}
|
||||
|
||||
func (t *Table) parseDimension(str string, colKey, rowKey int) []string {
|
||||
var (
|
||||
raw []string
|
||||
max int
|
||||
)
|
||||
w := DisplayWidth(str)
|
||||
// Calculate Width
|
||||
// Check if with is grater than maximum width
|
||||
if w > t.mW {
|
||||
w = t.mW
|
||||
}
|
||||
|
||||
// Check if width exists
|
||||
v, ok := t.cs[colKey]
|
||||
if !ok || v < w || v == 0 {
|
||||
t.cs[colKey] = w
|
||||
}
|
||||
|
||||
if rowKey == -1 {
|
||||
return raw
|
||||
}
|
||||
// Calculate Height
|
||||
if t.autoWrap {
|
||||
raw, _ = WrapString(str, t.cs[colKey])
|
||||
} else {
|
||||
raw = getLines(str)
|
||||
}
|
||||
|
||||
for _, line := range raw {
|
||||
if w := DisplayWidth(line); w > max {
|
||||
max = w
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure the with is the same length as maximum word
|
||||
// Important for cases where the width is smaller than maxu word
|
||||
if max > t.cs[colKey] {
|
||||
t.cs[colKey] = max
|
||||
}
|
||||
|
||||
h := len(raw)
|
||||
v, ok = t.rs[rowKey]
|
||||
|
||||
if !ok || v < h || v == 0 {
|
||||
t.rs[rowKey] = h
|
||||
}
|
||||
//fmt.Printf("Raw %+v %d\n", raw, len(raw))
|
||||
return raw
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
// Copyright 2014 Oleku Konko All rights reserved.
|
||||
// Use of this source code is governed by a MIT
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// This module is a Table Writer API for the Go Programming Language.
|
||||
// The protocols were written in pure Go and works on windows and unix systems
|
||||
|
||||
package tablewriter
|
||||
|
||||
import (
|
||||
"math"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/mattn/go-runewidth"
|
||||
)
|
||||
|
||||
var ansi = regexp.MustCompile("\033\\[(?:[0-9]{1,3}(?:;[0-9]{1,3})*)?[m|K]")
|
||||
|
||||
func DisplayWidth(str string) int {
|
||||
return runewidth.StringWidth(ansi.ReplaceAllLiteralString(str, ""))
|
||||
}
|
||||
|
||||
// Simple Condition for string
|
||||
// Returns value based on condition
|
||||
func ConditionString(cond bool, valid, inValid string) string {
|
||||
if cond {
|
||||
return valid
|
||||
}
|
||||
return inValid
|
||||
}
|
||||
|
||||
// Format Table Header
|
||||
// Replace _ , . and spaces
|
||||
func Title(name string) string {
|
||||
name = strings.Replace(name, "_", " ", -1)
|
||||
name = strings.Replace(name, ".", " ", -1)
|
||||
name = strings.TrimSpace(name)
|
||||
return strings.ToUpper(name)
|
||||
}
|
||||
|
||||
// Pad String
|
||||
// Attempts to play string in the center
|
||||
func Pad(s, pad string, width int) string {
|
||||
gap := width - DisplayWidth(s)
|
||||
if gap > 0 {
|
||||
gapLeft := int(math.Ceil(float64(gap / 2)))
|
||||
gapRight := gap - gapLeft
|
||||
return strings.Repeat(string(pad), gapLeft) + s + strings.Repeat(string(pad), gapRight)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// Pad String Right position
|
||||
// This would pace string at the left side fo the screen
|
||||
func PadRight(s, pad string, width int) string {
|
||||
gap := width - DisplayWidth(s)
|
||||
if gap > 0 {
|
||||
return s + strings.Repeat(string(pad), gap)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// Pad String Left position
|
||||
// This would pace string at the right side fo the screen
|
||||
func PadLeft(s, pad string, width int) string {
|
||||
gap := width - DisplayWidth(s)
|
||||
if gap > 0 {
|
||||
return strings.Repeat(string(pad), gap) + s
|
||||
}
|
||||
return s
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
// Copyright 2014 Oleku Konko All rights reserved.
|
||||
// Use of this source code is governed by a MIT
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// This module is a Table Writer API for the Go Programming Language.
|
||||
// The protocols were written in pure Go and works on windows and unix systems
|
||||
|
||||
package tablewriter
|
||||
|
||||
import (
|
||||
"math"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
var (
|
||||
nl = "\n"
|
||||
sp = " "
|
||||
)
|
||||
|
||||
const defaultPenalty = 1e5
|
||||
|
||||
// Wrap wraps s into a paragraph of lines of length lim, with minimal
|
||||
// raggedness.
|
||||
func WrapString(s string, lim int) ([]string, int) {
|
||||
words := strings.Split(strings.Replace(strings.TrimSpace(s), nl, sp, -1), sp)
|
||||
var lines []string
|
||||
max := 0
|
||||
for _, v := range words {
|
||||
max = len(v)
|
||||
if max > lim {
|
||||
lim = max
|
||||
}
|
||||
}
|
||||
for _, line := range WrapWords(words, 1, lim, defaultPenalty) {
|
||||
lines = append(lines, strings.Join(line, sp))
|
||||
}
|
||||
return lines, lim
|
||||
}
|
||||
|
||||
// WrapWords is the low-level line-breaking algorithm, useful if you need more
|
||||
// control over the details of the text wrapping process. For most uses,
|
||||
// WrapString will be sufficient and more convenient.
|
||||
//
|
||||
// WrapWords splits a list of words into lines with minimal "raggedness",
|
||||
// treating each rune as one unit, accounting for spc units between adjacent
|
||||
// words on each line, and attempting to limit lines to lim units. Raggedness
|
||||
// is the total error over all lines, where error is the square of the
|
||||
// difference of the length of the line and lim. Too-long lines (which only
|
||||
// happen when a single word is longer than lim units) have pen penalty units
|
||||
// added to the error.
|
||||
func WrapWords(words []string, spc, lim, pen int) [][]string {
|
||||
n := len(words)
|
||||
|
||||
length := make([][]int, n)
|
||||
for i := 0; i < n; i++ {
|
||||
length[i] = make([]int, n)
|
||||
length[i][i] = utf8.RuneCountInString(words[i])
|
||||
for j := i + 1; j < n; j++ {
|
||||
length[i][j] = length[i][j-1] + spc + utf8.RuneCountInString(words[j])
|
||||
}
|
||||
}
|
||||
nbrk := make([]int, n)
|
||||
cost := make([]int, n)
|
||||
for i := range cost {
|
||||
cost[i] = math.MaxInt32
|
||||
}
|
||||
for i := n - 1; i >= 0; i-- {
|
||||
if length[i][n-1] <= lim {
|
||||
cost[i] = 0
|
||||
nbrk[i] = n
|
||||
} else {
|
||||
for j := i + 1; j < n; j++ {
|
||||
d := lim - length[i][j-1]
|
||||
c := d*d + cost[j]
|
||||
if length[i][j-1] > lim {
|
||||
c += pen // too-long lines get a worse penalty
|
||||
}
|
||||
if c < cost[i] {
|
||||
cost[i] = c
|
||||
nbrk[i] = j
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
var lines [][]string
|
||||
i := 0
|
||||
for i < n {
|
||||
lines = append(lines, words[i:nbrk[i]])
|
||||
i = nbrk[i]
|
||||
}
|
||||
return lines
|
||||
}
|
||||
|
||||
// getLines decomposes a multiline string into a slice of strings.
|
||||
func getLines(s string) []string {
|
||||
var lines []string
|
||||
|
||||
for _, line := range strings.Split(strings.TrimSpace(s), nl) {
|
||||
lines = append(lines, line)
|
||||
}
|
||||
return lines
|
||||
}
|
Loading…
Reference in New Issue