Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: jedib0t/go-pretty
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v6.4.8
Choose a base ref
...
head repository: jedib0t/go-pretty
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v6.4.9
Choose a head ref
  • 1 commit
  • 17 files changed
  • 1 contributor

Commits on Oct 27, 2023

  1. Copy the full SHA
    64ffc58 View commit details
Showing with 971 additions and 868 deletions.
  1. +9 −9 list/render.go
  2. +18 −17 list/render_html.go
  3. +9 −9 list/render_markdown.go
  4. +4 −4 progress/tracker_sort.go
  5. +9 −0 sonar-project.properties
  6. +10 −9 table/render.go
  7. +765 −0 table/render_automerge_test.go
  8. +6 −5 table/render_csv.go
  9. +44 −43 table/render_html.go
  10. +7 −6 table/render_markdown.go
  11. +0 −686 table/render_test.go
  12. +17 −19 table/table.go
  13. +5 −5 text/align.go
  14. +4 −2 text/ansi.go
  15. +49 −39 text/string.go
  16. +3 −3 text/transformer.go
  17. +12 −12 text/valign.go
18 changes: 9 additions & 9 deletions list/render.go
Original file line number Diff line number Diff line change
@@ -6,15 +6,15 @@ import (
)

// Render renders the List in a human-readable "pretty" format. Example:
// * Game Of Thrones
// * Winter
// * Is
// * Coming
// * This
// * Is
// * Known
// * The Dark Tower
// * The Gunslinger
// | * Game Of Thrones
// | * Winter
// | * Is
// | * Coming
// | * This
// | * Is
// | * Known
// | * The Dark Tower
// | * The Gunslinger
func (l *List) Render() string {
l.initForRender()

35 changes: 18 additions & 17 deletions list/render_html.go
Original file line number Diff line number Diff line change
@@ -7,23 +7,24 @@ import (
)

// RenderHTML renders the List in the HTML format. Example:
// <ul class="go-pretty-table">
// <li>Game Of Thrones</li>
// <ul class="go-pretty-table-1">
// <li>Winter</li>
// <li>Is</li>
// <li>Coming</li>
// <ul class="go-pretty-table-2">
// <li>This</li>
// <li>Is</li>
// <li>Known</li>
// </ul>
// </ul>
// <li>The Dark Tower</li>
// <ul class="go-pretty-table-1">
// <li>The Gunslinger</li>
// </ul>
// </ul>
//
// <ul class="go-pretty-table">
// <li>Game Of Thrones</li>
// <ul class="go-pretty-table-1">
// <li>Winter</li>
// <li>Is</li>
// <li>Coming</li>
// <ul class="go-pretty-table-2">
// <li>This</li>
// <li>Is</li>
// <li>Known</li>
// </ul>
// </ul>
// <li>The Dark Tower</li>
// <ul class="go-pretty-table-1">
// <li>The Gunslinger</li>
// </ul>
// </ul>
func (l *List) RenderHTML() string {
l.initForRender()

18 changes: 9 additions & 9 deletions list/render_markdown.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
package list

// RenderMarkdown renders the List in the Markdown format. Example:
// * Game Of Thrones
// * Winter
// * Is
// * Coming
// * This
// * Is
// * Known
// * The Dark Tower
// * The Gunslinger
// | * Game Of Thrones
// | * Winter
// | * Is
// | * Coming
// | * This
// | * Is
// | * Known
// | * The Dark Tower
// | * The Gunslinger
func (l *List) RenderMarkdown() string {
// make a copy of the original style and ensure it is restored on exit
originalStyle := l.style
8 changes: 4 additions & 4 deletions progress/tracker_sort.go
Original file line number Diff line number Diff line change
@@ -56,8 +56,8 @@ func (sb sortByMessage) Less(i, j int) bool { return sb[i].message() < sb[j].mes

type sortByPercent []*Tracker

func (sb sortByPercent) Len() int { return len(sb) }
func (sb sortByPercent) Swap(i, j int) { sb[i], sb[j] = sb[j], sb[i] }
func (sb sortByPercent) Len() int { return len(sb) }
func (sb sortByPercent) Swap(i, j int) { sb[i], sb[j] = sb[j], sb[i] }
func (sb sortByPercent) Less(i, j int) bool {
if sb[i].PercentDone() == sb[j].PercentDone() {
return sb[i].timeStart.Before(sb[j].timeStart)
@@ -67,8 +67,8 @@ func (sb sortByPercent) Less(i, j int) bool {

type sortByValue []*Tracker

func (sb sortByValue) Len() int { return len(sb) }
func (sb sortByValue) Swap(i, j int) { sb[i], sb[j] = sb[j], sb[i] }
func (sb sortByValue) Len() int { return len(sb) }
func (sb sortByValue) Swap(i, j int) { sb[i], sb[j] = sb[j], sb[i] }
func (sb sortByValue) Less(i, j int) bool {
if sb[i].value == sb[j].value {
return sb[i].timeStart.Before(sb[j].timeStart)
9 changes: 9 additions & 0 deletions sonar-project.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Define the root directory for sources and tests
sonar.sources = ./
sonar.tests = ./

# Include test files in test scope
sonar.test.inclusions = **/*_test.go

# Exclude test files from source scope
sonar.exclusions = **/*_test.go
19 changes: 10 additions & 9 deletions table/render.go
Original file line number Diff line number Diff line change
@@ -9,15 +9,16 @@ import (
)

// Render renders the Table in a human-readable "pretty" format. Example:
// ┌─────┬────────────┬───────────┬────────┬─────────────────────────────┐
// │ # │ FIRST NAME │ LAST NAME │ SALARY │ │
// ├─────┼────────────┼───────────┼────────┼─────────────────────────────┤
// │ 1 │ Arya │ Stark │ 3000 │ │
// │ 20 │ Jon │ Snow │ 2000 │ You know nothing, Jon Snow! │
// │ 300 │ Tyrion │ Lannister │ 5000 │ │
// ├─────┼────────────┼───────────┼────────┼─────────────────────────────┤
// │ │ │ TOTAL │ 10000 │ │
// └─────┴────────────┴───────────┴────────┴─────────────────────────────┘
//
// ┌─────┬────────────┬───────────┬────────┬─────────────────────────────┐
// │ # │ FIRST NAME │ LAST NAME │ SALARY │ │
// ├─────┼────────────┼───────────┼────────┼─────────────────────────────┤
// │ 1 │ Arya │ Stark │ 3000 │ │
// │ 20 │ Jon │ Snow │ 2000 │ You know nothing, Jon Snow! │
// │ 300 │ Tyrion │ Lannister │ 5000 │ │
// ├─────┼────────────┼───────────┼────────┼─────────────────────────────┤
// │ │ │ TOTAL │ 10000 │ │
// └─────┴────────────┴───────────┴────────┴─────────────────────────────┘
func (t *Table) Render() string {
t.initForRender()

765 changes: 765 additions & 0 deletions table/render_automerge_test.go

Large diffs are not rendered by default.

11 changes: 6 additions & 5 deletions table/render_csv.go
Original file line number Diff line number Diff line change
@@ -7,11 +7,12 @@ import (
)

// RenderCSV renders the Table in CSV format. Example:
// #,First Name,Last Name,Salary,
// 1,Arya,Stark,3000,
// 20,Jon,Snow,2000,"You know nothing\, Jon Snow!"
// 300,Tyrion,Lannister,5000,
// ,,Total,10000,
//
// #,First Name,Last Name,Salary,
// 1,Arya,Stark,3000,
// 20,Jon,Snow,2000,"You know nothing\, Jon Snow!"
// 300,Tyrion,Lannister,5000,
// ,,Total,10000,
func (t *Table) RenderCSV() string {
t.initForRender()

87 changes: 44 additions & 43 deletions table/render_html.go
Original file line number Diff line number Diff line change
@@ -13,49 +13,50 @@ const (
)

// RenderHTML renders the Table in HTML format. Example:
// <table class="go-pretty-table">
// <thead>
// <tr>
// <th align="right">#</th>
// <th>First Name</th>
// <th>Last Name</th>
// <th align="right">Salary</th>
// <th>&nbsp;</th>
// </tr>
// </thead>
// <tbody>
// <tr>
// <td align="right">1</td>
// <td>Arya</td>
// <td>Stark</td>
// <td align="right">3000</td>
// <td>&nbsp;</td>
// </tr>
// <tr>
// <td align="right">20</td>
// <td>Jon</td>
// <td>Snow</td>
// <td align="right">2000</td>
// <td>You know nothing, Jon Snow!</td>
// </tr>
// <tr>
// <td align="right">300</td>
// <td>Tyrion</td>
// <td>Lannister</td>
// <td align="right">5000</td>
// <td>&nbsp;</td>
// </tr>
// </tbody>
// <tfoot>
// <tr>
// <td align="right">&nbsp;</td>
// <td>&nbsp;</td>
// <td>Total</td>
// <td align="right">10000</td>
// <td>&nbsp;</td>
// </tr>
// </tfoot>
// </table>
//
// <table class="go-pretty-table">
// <thead>
// <tr>
// <th align="right">#</th>
// <th>First Name</th>
// <th>Last Name</th>
// <th align="right">Salary</th>
// <th>&nbsp;</th>
// </tr>
// </thead>
// <tbody>
// <tr>
// <td align="right">1</td>
// <td>Arya</td>
// <td>Stark</td>
// <td align="right">3000</td>
// <td>&nbsp;</td>
// </tr>
// <tr>
// <td align="right">20</td>
// <td>Jon</td>
// <td>Snow</td>
// <td align="right">2000</td>
// <td>You know nothing, Jon Snow!</td>
// </tr>
// <tr>
// <td align="right">300</td>
// <td>Tyrion</td>
// <td>Lannister</td>
// <td align="right">5000</td>
// <td>&nbsp;</td>
// </tr>
// </tbody>
// <tfoot>
// <tr>
// <td align="right">&nbsp;</td>
// <td>&nbsp;</td>
// <td>Total</td>
// <td align="right">10000</td>
// <td>&nbsp;</td>
// </tr>
// </tfoot>
// </table>
func (t *Table) RenderHTML() string {
t.initForRender()

13 changes: 7 additions & 6 deletions table/render_markdown.go
Original file line number Diff line number Diff line change
@@ -6,12 +6,13 @@ import (
)

// RenderMarkdown renders the Table in Markdown format. Example:
// | # | First Name | Last Name | Salary | |
// | ---:| --- | --- | ---:| --- |
// | 1 | Arya | Stark | 3000 | |
// | 20 | Jon | Snow | 2000 | You know nothing, Jon Snow! |
// | 300 | Tyrion | Lannister | 5000 | |
// | | | Total | 10000 | |
//
// | # | First Name | Last Name | Salary | |
// | ---:| --- | --- | ---:| --- |
// | 1 | Arya | Stark | 3000 | |
// | 20 | Jon | Snow | 2000 | You know nothing, Jon Snow! |
// | 300 | Tyrion | Lannister | 5000 | |
// | | | Total | 10000 | |
func (t *Table) RenderMarkdown() string {
t.initForRender()

686 changes: 0 additions & 686 deletions table/render_test.go

Large diffs are not rendered by default.

36 changes: 17 additions & 19 deletions table/table.go
Original file line number Diff line number Diff line change
@@ -164,14 +164,15 @@ func (t *Table) AppendRows(rows []Row, config ...RowConfig) {
// append is a separator, it will not be rendered in addition to the usual table
// separator.
//
//******************************************************************************
// ******************************************************************************
// Please note the following caveats:
// 1. SetPageSize(): this may end up creating consecutive separator rows near
// the end of a page or at the beginning of a page
// 2. SortBy(): since SortBy could inherently alter the ordering of rows, the
// separators may not appear after the row it was originally intended to
// follow
//******************************************************************************
// 1. SetPageSize(): this may end up creating consecutive separator rows near
// the end of a page or at the beginning of a page
// 2. SortBy(): since SortBy could inherently alter the ordering of rows, the
// separators may not appear after the row it was originally intended to
// follow
//
// ******************************************************************************
func (t *Table) AppendSeparator() {
if t.separators == nil {
t.separators = make(map[int]bool)
@@ -439,12 +440,9 @@ func (t *Table) getColumnSeparator(row rowStr, colIdx int, hint renderHint) stri
separator = t.style.Box.BottomSeparator
}
} else {
separator = t.getColumnSeparatorNonBorder(
t.shouldMergeCellsHorizontallyAbove(row, colIdx, hint),
t.shouldMergeCellsHorizontallyBelow(row, colIdx, hint),
colIdx,
hint,
)
sm1 := t.shouldMergeCellsHorizontallyAbove(row, colIdx, hint)
sm2 := t.shouldMergeCellsHorizontallyBelow(row, colIdx, hint)
separator = t.getColumnSeparatorNonBorder(sm1, sm2, colIdx, hint)
}
}
return separator
@@ -721,7 +719,10 @@ func (t *Table) shouldMergeCellsHorizontallyBelow(row rowStr, colIdx int, hint r

var rowConfig RowConfig
if hint.isSeparatorRow {
if hint.isHeaderRow && hint.rowNumber == 0 {
if hint.isRegularRow() {
rowConfig = t.getRowConfig(renderHint{rowNumber: hint.rowNumber + 1})
row = t.getRow(hint.rowNumber, renderHint{})
} else if hint.isHeaderRow && hint.rowNumber == 0 {
rowConfig = t.getRowConfig(renderHint{isHeaderRow: true, rowNumber: 1})
row = t.getRow(0, hint)
} else if hint.isHeaderRow && hint.isLastRow {
@@ -733,9 +734,6 @@ func (t *Table) shouldMergeCellsHorizontallyBelow(row rowStr, colIdx int, hint r
} else if hint.isFooterRow && hint.rowNumber >= 0 {
rowConfig = t.getRowConfig(renderHint{isFooterRow: true, rowNumber: 1})
row = t.getRow(hint.rowNumber, renderHint{isFooterRow: true})
} else if hint.isRegularRow() {
rowConfig = t.getRowConfig(renderHint{rowNumber: hint.rowNumber + 1})
row = t.getRow(hint.rowNumber, renderHint{})
}
}

@@ -751,13 +749,13 @@ func (t *Table) shouldMergeCellsVertically(colIdx int, hint renderHint) bool {
rowPrev := t.getRow(hint.rowNumber-1, hint)
rowNext := t.getRow(hint.rowNumber, hint)
if colIdx < len(rowPrev) && colIdx < len(rowNext) {
return rowPrev[colIdx] == rowNext[colIdx] || rowNext[colIdx] == ""
return rowPrev[colIdx] == rowNext[colIdx]
}
} else {
rowPrev := t.getRow(hint.rowNumber-2, hint)
rowCurr := t.getRow(hint.rowNumber-1, hint)
if colIdx < len(rowPrev) && colIdx < len(rowCurr) {
return rowPrev[colIdx] == rowCurr[colIdx] || rowCurr[colIdx] == ""
return rowPrev[colIdx] == rowCurr[colIdx]
}
}
}
10 changes: 5 additions & 5 deletions text/align.go
Original file line number Diff line number Diff line change
@@ -20,11 +20,11 @@ const (
)

// Apply aligns the text as directed. For ex.:
// * AlignDefault.Apply("Jon Snow", 12) returns "Jon Snow "
// * AlignLeft.Apply("Jon Snow", 12) returns "Jon Snow "
// * AlignCenter.Apply("Jon Snow", 12) returns " Jon Snow "
// * AlignJustify.Apply("Jon Snow", 12) returns "Jon Snow"
// * AlignRight.Apply("Jon Snow", 12) returns " Jon Snow"
// - AlignDefault.Apply("Jon Snow", 12) returns "Jon Snow "
// - AlignLeft.Apply("Jon Snow", 12) returns "Jon Snow "
// - AlignCenter.Apply("Jon Snow", 12) returns " Jon Snow "
// - AlignJustify.Apply("Jon Snow", 12) returns "Jon Snow"
// - AlignRight.Apply("Jon Snow", 12) returns " Jon Snow"
func (a Align) Apply(text string, maxLength int) string {
text = a.trimString(text)
sLen := utf8.RuneCountInString(text)
6 changes: 4 additions & 2 deletions text/ansi.go
Original file line number Diff line number Diff line change
@@ -8,7 +8,8 @@ var ANSICodesSupported = areANSICodesSupported()

// Escape encodes the string with the ANSI Escape Sequence.
// For ex.:
// Escape("Ghost", "") == "Ghost"
//
// Escape("Ghost", "") == "Ghost"
// Escape("Ghost", "\x1b[91m") == "\x1b[91mGhost\x1b[0m"
// Escape("\x1b[94mGhost\x1b[0mLady", "\x1b[91m") == "\x1b[94mGhost\x1b[0m\x1b[91mLady\x1b[0m"
// Escape("Nymeria\x1b[94mGhost\x1b[0mLady", "\x1b[91m") == "\x1b[91mNymeria\x1b[94mGhost\x1b[0m\x1b[91mLady\x1b[0m"
@@ -30,7 +31,8 @@ func Escape(str string, escapeSeq string) string {

// StripEscape strips all ANSI Escape Sequence from the string.
// For ex.:
// StripEscape("Ghost") == "Ghost"
//
// StripEscape("Ghost") == "Ghost"
// StripEscape("\x1b[91mGhost\x1b[0m") == "Ghost"
// StripEscape("\x1b[94mGhost\x1b[0m\x1b[91mLady\x1b[0m") == "GhostLady"
// StripEscape("\x1b[91mNymeria\x1b[94mGhost\x1b[0m\x1b[91mLady\x1b[0m") == "NymeriaGhostLady"
88 changes: 49 additions & 39 deletions text/string.go
Original file line number Diff line number Diff line change
@@ -13,11 +13,12 @@ var (
)

// InsertEveryN inserts the rune every N characters in the string. For ex.:
// InsertEveryN("Ghost", '-', 1) == "G-h-o-s-t"
// InsertEveryN("Ghost", '-', 2) == "Gh-os-t"
// InsertEveryN("Ghost", '-', 3) == "Gho-st"
// InsertEveryN("Ghost", '-', 4) == "Ghos-t"
// InsertEveryN("Ghost", '-', 5) == "Ghost"
//
// InsertEveryN("Ghost", '-', 1) == "G-h-o-s-t"
// InsertEveryN("Ghost", '-', 2) == "Gh-os-t"
// InsertEveryN("Ghost", '-', 3) == "Gho-st"
// InsertEveryN("Ghost", '-', 4) == "Ghos-t"
// InsertEveryN("Ghost", '-', 5) == "Ghost"
func InsertEveryN(str string, runeToInsert rune, n int) string {
if n <= 0 {
return str
@@ -47,7 +48,8 @@ func InsertEveryN(str string, runeToInsert rune, n int) string {

// LongestLineLen returns the length of the longest "line" within the
// argument string. For ex.:
// LongestLineLen("Ghost!\nCome back here!\nRight now!") == 15
//
// LongestLineLen("Ghost!\nCome back here!\nRight now!") == 15
func LongestLineLen(str string) int {
maxLength, currLength, eSeq := 0, 0, escSeq{}
for _, c := range str {
@@ -91,11 +93,12 @@ func OverrideRuneWidthEastAsianWidth(val bool) {
// Pad pads the given string with as many characters as needed to make it as
// long as specified (maxLen). This function does not count escape sequences
// while calculating length of the string. Ex.:
// Pad("Ghost", 0, ' ') == "Ghost"
// Pad("Ghost", 3, ' ') == "Ghost"
// Pad("Ghost", 5, ' ') == "Ghost"
// Pad("Ghost", 7, ' ') == "Ghost "
// Pad("Ghost", 10, '.') == "Ghost....."
//
// Pad("Ghost", 0, ' ') == "Ghost"
// Pad("Ghost", 3, ' ') == "Ghost"
// Pad("Ghost", 5, ' ') == "Ghost"
// Pad("Ghost", 7, ' ') == "Ghost "
// Pad("Ghost", 10, '.') == "Ghost....."
func Pad(str string, maxLen int, paddingChar rune) string {
strLen := RuneWidthWithoutEscSequences(str)
if strLen < maxLen {
@@ -106,11 +109,12 @@ func Pad(str string, maxLen int, paddingChar rune) string {

// RepeatAndTrim repeats the given string until it is as long as maxRunes.
// For ex.:
// RepeatAndTrim("", 5) == ""
// RepeatAndTrim("Ghost", 0) == ""
// RepeatAndTrim("Ghost", 5) == "Ghost"
// RepeatAndTrim("Ghost", 7) == "GhostGh"
// RepeatAndTrim("Ghost", 10) == "GhostGhost"
//
// RepeatAndTrim("", 5) == ""
// RepeatAndTrim("Ghost", 0) == ""
// RepeatAndTrim("Ghost", 5) == "Ghost"
// RepeatAndTrim("Ghost", 7) == "GhostGh"
// RepeatAndTrim("Ghost", 10) == "GhostGhost"
func RepeatAndTrim(str string, maxRunes int) string {
if str == "" || maxRunes == 0 {
return ""
@@ -123,10 +127,12 @@ func RepeatAndTrim(str string, maxRunes int) string {

// RuneCount is similar to utf8.RuneCountInString, except for the fact that it
// ignores escape sequences while counting. For ex.:
// RuneCount("") == 0
// RuneCount("Ghost") == 5
// RuneCount("\x1b[33mGhost\x1b[0m") == 5
// RuneCount("\x1b[33mGhost\x1b[0") == 5
//
// RuneCount("") == 0
// RuneCount("Ghost") == 5
// RuneCount("\x1b[33mGhost\x1b[0m") == 5
// RuneCount("\x1b[33mGhost\x1b[0") == 5
//
// Deprecated: in favor of RuneWidthWithoutEscSequences
func RuneCount(str string) int {
return RuneWidthWithoutEscSequences(str)
@@ -135,21 +141,23 @@ func RuneCount(str string) int {
// RuneWidth returns the mostly accurate character-width of the rune. This is
// not 100% accurate as the character width is usually dependent on the
// typeface (font) used in the console/terminal. For ex.:
// RuneWidth('A') == 1
// RuneWidth('ツ') == 2
// RuneWidth('⊙') == 1
// RuneWidth('︿') == 2
// RuneWidth(0x27) == 0
//
// RuneWidth('A') == 1
// RuneWidth('ツ') == 2
// RuneWidth('⊙') == 1
// RuneWidth('︿') == 2
// RuneWidth(0x27) == 0
func RuneWidth(r rune) int {
return rwCondition.RuneWidth(r)
}

// RuneWidthWithoutEscSequences is similar to RuneWidth, except for the fact
// that it ignores escape sequences while counting. For ex.:
// RuneWidthWithoutEscSequences("") == 0
// RuneWidthWithoutEscSequences("Ghost") == 5
// RuneWidthWithoutEscSequences("\x1b[33mGhost\x1b[0m") == 5
// RuneWidthWithoutEscSequences("\x1b[33mGhost\x1b[0") == 5
//
// RuneWidthWithoutEscSequences("") == 0
// RuneWidthWithoutEscSequences("Ghost") == 5
// RuneWidthWithoutEscSequences("\x1b[33mGhost\x1b[0m") == 5
// RuneWidthWithoutEscSequences("\x1b[33mGhost\x1b[0") == 5
func RuneWidthWithoutEscSequences(str string) int {
count, eSeq := 0, escSeq{}
for _, c := range str {
@@ -166,12 +174,13 @@ func RuneWidthWithoutEscSequences(str string) int {
}

// Snip returns the given string with a fixed length. For ex.:
// Snip("Ghost", 0, "~") == "Ghost"
// Snip("Ghost", 1, "~") == "~"
// Snip("Ghost", 3, "~") == "Gh~"
// Snip("Ghost", 5, "~") == "Ghost"
// Snip("Ghost", 7, "~") == "Ghost "
// Snip("\x1b[33mGhost\x1b[0m", 7, "~") == "\x1b[33mGhost\x1b[0m "
//
// Snip("Ghost", 0, "~") == "Ghost"
// Snip("Ghost", 1, "~") == "~"
// Snip("Ghost", 3, "~") == "Gh~"
// Snip("Ghost", 5, "~") == "Ghost"
// Snip("Ghost", 7, "~") == "Ghost "
// Snip("\x1b[33mGhost\x1b[0m", 7, "~") == "\x1b[33mGhost\x1b[0m "
func Snip(str string, length int, snipIndicator string) string {
if length > 0 {
lenStr := RuneWidthWithoutEscSequences(str)
@@ -185,10 +194,11 @@ func Snip(str string, length int, snipIndicator string) string {

// Trim trims a string to the given length while ignoring escape sequences. For
// ex.:
// Trim("Ghost", 3) == "Gho"
// Trim("Ghost", 6) == "Ghost"
// Trim("\x1b[33mGhost\x1b[0m", 3) == "\x1b[33mGho\x1b[0m"
// Trim("\x1b[33mGhost\x1b[0m", 6) == "\x1b[33mGhost\x1b[0m"
//
// Trim("Ghost", 3) == "Gho"
// Trim("Ghost", 6) == "Ghost"
// Trim("\x1b[33mGhost\x1b[0m", 3) == "\x1b[33mGho\x1b[0m"
// Trim("\x1b[33mGhost\x1b[0m", 6) == "\x1b[33mGhost\x1b[0m"
func Trim(str string, maxLen int) string {
if maxLen <= 0 {
return ""
6 changes: 3 additions & 3 deletions text/transformer.go
Original file line number Diff line number Diff line change
@@ -37,9 +37,9 @@ var (
type Transformer func(val interface{}) string

// NewNumberTransformer returns a number Transformer that:
// * transforms the number as directed by 'format' (ex.: %.2f)
// * colors negative values Red
// * colors positive values Green
// - transforms the number as directed by 'format' (ex.: %.2f)
// - colors negative values Red
// - colors positive values Green
func NewNumberTransformer(format string) Transformer {
return func(val interface{}) string {
if valStr := transformInt(format, val); valStr != "" {
24 changes: 12 additions & 12 deletions text/valign.go
Original file line number Diff line number Diff line change
@@ -14,12 +14,12 @@ const (
)

// Apply aligns the lines vertically. For ex.:
// * VAlignTop.Apply({"Game", "Of", "Thrones"}, 5)
// returns {"Game", "Of", "Thrones", "", ""}
// * VAlignMiddle.Apply({"Game", "Of", "Thrones"}, 5)
// returns {"", "Game", "Of", "Thrones", ""}
// * VAlignBottom.Apply({"Game", "Of", "Thrones"}, 5)
// returns {"", "", "Game", "Of", "Thrones"}
// - VAlignTop.Apply({"Game", "Of", "Thrones"}, 5)
// returns {"Game", "Of", "Thrones", "", ""}
// - VAlignMiddle.Apply({"Game", "Of", "Thrones"}, 5)
// returns {"", "Game", "Of", "Thrones", ""}
// - VAlignBottom.Apply({"Game", "Of", "Thrones"}, 5)
// returns {"", "", "Game", "Of", "Thrones"}
func (va VAlign) Apply(lines []string, maxLines int) []string {
if len(lines) == maxLines {
return lines
@@ -42,12 +42,12 @@ func (va VAlign) Apply(lines []string, maxLines int) []string {
}

// ApplyStr aligns the string (of 1 or more lines) vertically. For ex.:
// * VAlignTop.ApplyStr("Game\nOf\nThrones", 5)
// returns {"Game", "Of", "Thrones", "", ""}
// * VAlignMiddle.ApplyStr("Game\nOf\nThrones", 5)
// returns {"", "Game", "Of", "Thrones", ""}
// * VAlignBottom.ApplyStr("Game\nOf\nThrones", 5)
// returns {"", "", "Game", "Of", "Thrones"}
// - VAlignTop.ApplyStr("Game\nOf\nThrones", 5)
// returns {"Game", "Of", "Thrones", "", ""}
// - VAlignMiddle.ApplyStr("Game\nOf\nThrones", 5)
// returns {"", "Game", "Of", "Thrones", ""}
// - VAlignBottom.ApplyStr("Game\nOf\nThrones", 5)
// returns {"", "", "Game", "Of", "Thrones"}
func (va VAlign) ApplyStr(text string, maxLines int) []string {
return va.Apply(strings.Split(text, "\n"), maxLines)
}