Skip to content

Commit

Permalink
text: Format* can now deal with escape sequences; fixes #112
Browse files Browse the repository at this point in the history
  • Loading branch information
jedib0t committed May 13, 2020
1 parent 65260f6 commit 9e9f13b
Show file tree
Hide file tree
Showing 6 changed files with 132 additions and 13 deletions.
3 changes: 3 additions & 0 deletions table/render.go
Expand Up @@ -67,6 +67,9 @@ func (t *Table) renderColumn(out *strings.Builder, row rowStr, colIdx int, maxCo
if colIdx < len(row) && !mergeCurrCol {
colStr = t.getFormat(hint).Apply(row[colIdx])
}
if strings.Contains(colStr, "10000") {
fmt.Println()
}
colStr = t.getAlign(colIdx, hint).Apply(colStr, maxColumnLength)

// pad both sides of the column
Expand Down
2 changes: 0 additions & 2 deletions table/render_markdown_test.go
Expand Up @@ -2,7 +2,6 @@ package table

import (
"fmt"
"os"
"testing"

"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -43,7 +42,6 @@ func TestTable_RenderMarkdown_AutoIndex(t *testing.T) {
}
tw.SetAutoIndex(true)
tw.SetStyle(StyleLight)
tw.SetOutputMirror(os.Stdout)

expectedOut := `| | A | B | C | D | E | F | G | H | I | J |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
Expand Down
36 changes: 36 additions & 0 deletions table/render_test.go
Expand Up @@ -1008,3 +1008,39 @@ func TestTable_Render_TableWithinTable(t *testing.T) {
╚═════════════════════════════════════════════════════════════════════════╝`
assert.Equal(t, expectedOut, twOuter.Render())
}

func TestTable_Render_TableWithTransformers(t *testing.T) {
bolden := func(val interface{}) string {
return text.Bold.Sprint(val)
}
tw := NewWriter()
tw.AppendHeader(testHeader)
tw.AppendRows(testRows)
tw.AppendFooter(testFooter)
tw.SetColumnConfigs([]ColumnConfig{{
Name: "Salary",
Transformer: bolden,
TransformerFooter: bolden,
TransformerHeader: bolden,
}})
tw.SetStyle(StyleLight)

expectedOut := []string{
"┌─────┬────────────┬───────────┬────────┬─────────────────────────────┐",
"│ # │ FIRST NAME │ LAST NAME │ \x1b[1mSALARY\x1b[0m │ │",
"├─────┼────────────┼───────────┼────────┼─────────────────────────────┤",
"│ 1 │ Arya │ Stark │ \x1b[1m3000\x1b[0m │ │",
"│ 20 │ Jon │ Snow │ \x1b[1m2000\x1b[0m │ You know nothing, Jon Snow! │",
"│ 300 │ Tyrion │ Lannister │ \x1b[1m5000\x1b[0m │ │",
"├─────┼────────────┼───────────┼────────┼─────────────────────────────┤",
"│ │ │ TOTAL │ \x1b[1m10000\x1b[0m │ │",
"└─────┴────────────┴───────────┴────────┴─────────────────────────────┘",
}
out := tw.Render()
assert.Equal(t, strings.Join(expectedOut, "\n"), out)
if strings.Join(expectedOut, "\n") != out {
for _, line := range strings.Split(out, "\n") {
fmt.Printf("%#v,\n", line)
}
}
}
4 changes: 4 additions & 0 deletions text/color_test.go
Expand Up @@ -7,6 +7,10 @@ import (
"github.com/stretchr/testify/assert"
)

func init() {
EnableColors()
}

func TestColor_EnableAndDisable(t *testing.T) {
defer EnableColors()

Expand Down
75 changes: 72 additions & 3 deletions text/format.go
@@ -1,6 +1,9 @@
package text

import "strings"
import (
"strings"
"unicode"
)

// Format denotes the "case" to use for text.
type Format int
Expand All @@ -19,10 +22,76 @@ func (tc Format) Apply(text string) string {
case FormatLower:
return strings.ToLower(text)
case FormatTitle:
return strings.Title(text)
return tc.toTitle(text)
case FormatUpper:
return strings.ToUpper(text)
return tc.toUpper(text)
default:
return text
}
}

func (tc Format) toUpper(text string) string {
inEscSeq := false
return strings.Map(
func(r rune) rune {
if r == EscapeStartRune {
inEscSeq = true
}
if !inEscSeq {
r = unicode.ToUpper(r)
}
if inEscSeq && r == EscapeStopRune {
inEscSeq = false
}
return r
},
text,
)
}

func (tc Format) toTitle(text string) string {
prev, inEscSeq := ' ', false
return strings.Map(
func(r rune) rune {
if r == EscapeStartRune {
inEscSeq = true
}
if !inEscSeq {
if tc.isSeparator(prev) {
prev = r
r = unicode.ToUpper(r)
} else {
prev = r
}
}
if inEscSeq && r == EscapeStopRune {
inEscSeq = false
}
return r
},
text,
)
}

func (tc Format) isSeparator(r rune) bool {
// ASCII alphanumerics and underscore are not separators
if r <= 0x7F {
switch {
case '0' <= r && r <= '9':
return false
case 'a' <= r && r <= 'z':
return false
case 'A' <= r && r <= 'Z':
return false
case r == '_':
return false
}
return true
}
// Letters and digits are not separators
if unicode.IsLetter(r) || unicode.IsDigit(r) {
return false
}
// Otherwise, all we can do for now is treat spaces as separators.
return unicode.IsSpace(r)
}
25 changes: 17 additions & 8 deletions text/format_test.go
Expand Up @@ -8,10 +8,11 @@ import (
)

func ExampleFormat_Apply() {
fmt.Printf("FormatDefault: '%s'\n", FormatDefault.Apply("jon Snow"))
fmt.Printf("FormatLower : '%s'\n", FormatLower.Apply("jon Snow"))
fmt.Printf("FormatTitle : '%s'\n", FormatTitle.Apply("jon Snow"))
fmt.Printf("FormatUpper : '%s'\n", FormatUpper.Apply("jon Snow"))
text := "jon Snow"
fmt.Printf("FormatDefault: '%s'\n", FormatDefault.Apply(text))
fmt.Printf("FormatLower : '%s'\n", FormatLower.Apply(text))
fmt.Printf("FormatTitle : '%s'\n", FormatTitle.Apply(text))
fmt.Printf("FormatUpper : '%s'\n", FormatUpper.Apply(text))

// Output: FormatDefault: 'jon Snow'
// FormatLower : 'jon snow'
Expand All @@ -20,8 +21,16 @@ func ExampleFormat_Apply() {
}

func TestFormat_Apply(t *testing.T) {
assert.Equal(t, "jon Snow", FormatDefault.Apply("jon Snow"))
assert.Equal(t, "jon snow", FormatLower.Apply("jon Snow"))
assert.Equal(t, "Jon Snow", FormatTitle.Apply("jon Snow"))
assert.Equal(t, "JON SNOW", FormatUpper.Apply("jon Snow"))
text := "A big crocodile, Died. Empty."
assert.Equal(t, text, FormatDefault.Apply(text))
assert.Equal(t, "a big crocodile, died. empty.", FormatLower.Apply(text))
assert.Equal(t, "A Big Crocodile, Died. Empty.", FormatTitle.Apply(text))
assert.Equal(t, "A BIG CROCODILE, DIED. EMPTY.", FormatUpper.Apply(text))

// test with escape sequences
text = Colors{Bold}.Sprint(text)
assert.Equal(t, "\x1b[1mA big crocodile, Died. Empty.\x1b[0m", FormatDefault.Apply(text))
assert.Equal(t, "\x1b[1ma big crocodile, died. empty.\x1b[0m", FormatLower.Apply(text))
assert.Equal(t, "\x1b[1mA Big Crocodile, Died. Empty.\x1b[0m", FormatTitle.Apply(text))
assert.Equal(t, "\x1b[1mA BIG CROCODILE, DIED. EMPTY.\x1b[0m", FormatUpper.Apply(text))
}

0 comments on commit 9e9f13b

Please sign in to comment.