mirror of
https://github.com/MichaelMure/git-bug.git
synced 2024-12-15 02:01:43 +03:00
util: add a text wrapping function
This commit is contained in:
parent
d88d59e9c5
commit
5c86164f22
@ -2,8 +2,8 @@ package termui
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/MichaelMure/git-bug/util"
|
||||
"github.com/jroimartin/gocui"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const errorPopupView = "errorPopupView"
|
||||
@ -37,7 +37,7 @@ func (ep *errorPopup) layout(g *gocui.Gui) error {
|
||||
maxX, maxY := g.Size()
|
||||
|
||||
width := minInt(30, maxX)
|
||||
wrapped, nblines := word_wrap(ep.message, width-2)
|
||||
wrapped, nblines := util.WordWrap(ep.message, width-2)
|
||||
height := minInt(nblines+2, maxY)
|
||||
x0 := (maxX - width) / 2
|
||||
y0 := (maxY - height) / 2
|
||||
@ -69,25 +69,3 @@ func (ep *errorPopup) close(g *gocui.Gui, v *gocui.View) error {
|
||||
func (ep *errorPopup) activate(message string) {
|
||||
ep.message = message
|
||||
}
|
||||
|
||||
func word_wrap(text string, lineWidth int) (string, int) {
|
||||
words := strings.Fields(strings.TrimSpace(text))
|
||||
if len(words) == 0 {
|
||||
return text, 1
|
||||
}
|
||||
lines := 1
|
||||
wrapped := words[0]
|
||||
spaceLeft := lineWidth - len(wrapped)
|
||||
for _, word := range words[1:] {
|
||||
if len(word)+1 > spaceLeft {
|
||||
wrapped += "\n" + word
|
||||
spaceLeft = lineWidth - len(word)
|
||||
lines++
|
||||
} else {
|
||||
wrapped += " " + word
|
||||
spaceLeft -= 1 + len(word)
|
||||
}
|
||||
}
|
||||
|
||||
return wrapped, lines
|
||||
}
|
||||
|
100
util/text.go
Normal file
100
util/text.go
Normal file
@ -0,0 +1,100 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func WordWrap(text string, lineWidth int) (string, int) {
|
||||
words := strings.Fields(strings.TrimSpace(text))
|
||||
if len(words) == 0 {
|
||||
return "", 1
|
||||
}
|
||||
lines := 1
|
||||
wrapped := words[0]
|
||||
spaceLeft := lineWidth - len(wrapped)
|
||||
for _, word := range words[1:] {
|
||||
if len(word)+1 > spaceLeft {
|
||||
wrapped += "\n" + word
|
||||
spaceLeft = lineWidth - len(word)
|
||||
lines++
|
||||
} else {
|
||||
wrapped += " " + word
|
||||
spaceLeft -= 1 + len(word)
|
||||
}
|
||||
}
|
||||
|
||||
return wrapped, lines
|
||||
}
|
||||
|
||||
func TextWrap(text string, lineWidth int) (string, int) {
|
||||
var textBuffer bytes.Buffer
|
||||
var lineBuffer bytes.Buffer
|
||||
nbLine := 1
|
||||
firstLine := true
|
||||
|
||||
// tabs are formatted as 4 spaces
|
||||
text = strings.Replace(text, "\t", " ", 4)
|
||||
|
||||
for _, line := range strings.Split(text, "\n") {
|
||||
spaceLeft := lineWidth
|
||||
|
||||
if !firstLine {
|
||||
textBuffer.WriteString("\n")
|
||||
nbLine++
|
||||
}
|
||||
|
||||
firstWord := true
|
||||
|
||||
for _, word := range strings.Split(line, " ") {
|
||||
if spaceLeft > len(word) {
|
||||
if !firstWord {
|
||||
lineBuffer.WriteString(" ")
|
||||
spaceLeft -= 1
|
||||
}
|
||||
lineBuffer.WriteString(word)
|
||||
spaceLeft -= len(word)
|
||||
firstWord = false
|
||||
} else {
|
||||
if len(word) > lineWidth {
|
||||
for len(word) > 0 {
|
||||
l := minInt(spaceLeft, len(word))
|
||||
part := word[:l]
|
||||
word = word[l:]
|
||||
|
||||
lineBuffer.WriteString(part)
|
||||
textBuffer.Write(lineBuffer.Bytes())
|
||||
lineBuffer.Reset()
|
||||
|
||||
if len(word) > 0 {
|
||||
textBuffer.WriteString("\n")
|
||||
nbLine++
|
||||
}
|
||||
|
||||
spaceLeft = lineWidth
|
||||
}
|
||||
} else {
|
||||
textBuffer.WriteString(strings.TrimRight(lineBuffer.String(), " "))
|
||||
textBuffer.WriteString("\n")
|
||||
lineBuffer.Reset()
|
||||
lineBuffer.WriteString(word)
|
||||
firstWord = false
|
||||
spaceLeft = lineWidth - len(word)
|
||||
nbLine++
|
||||
}
|
||||
}
|
||||
}
|
||||
textBuffer.WriteString(strings.TrimRight(lineBuffer.String(), " "))
|
||||
lineBuffer.Reset()
|
||||
firstLine = false
|
||||
}
|
||||
|
||||
return textBuffer.String(), nbLine
|
||||
}
|
||||
|
||||
func minInt(a, b int) int {
|
||||
if a > b {
|
||||
return b
|
||||
}
|
||||
return a
|
||||
}
|
89
util/text_test.go
Normal file
89
util/text_test.go
Normal file
@ -0,0 +1,89 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestTextWrap(t *testing.T) {
|
||||
cases := []struct {
|
||||
Input, Output string
|
||||
Lim int
|
||||
}{
|
||||
// A simple word passes through.
|
||||
{
|
||||
"foo",
|
||||
"foo",
|
||||
4,
|
||||
},
|
||||
// Word breaking
|
||||
{
|
||||
"foobarbaz",
|
||||
"foob\narba\nz",
|
||||
4,
|
||||
},
|
||||
// Lines are broken at whitespace.
|
||||
{
|
||||
"foo bar baz",
|
||||
"foo\nbar\nbaz",
|
||||
4,
|
||||
},
|
||||
// Word breaking
|
||||
{
|
||||
"foo bars bazzes",
|
||||
"foo\nbars\nbazz\nes",
|
||||
4,
|
||||
},
|
||||
// A word that would run beyond the width is wrapped.
|
||||
{
|
||||
"fo sop",
|
||||
"fo\nsop",
|
||||
4,
|
||||
},
|
||||
// A tab counts as 4 characters.
|
||||
{
|
||||
"foo\nb\t r\n baz",
|
||||
"foo\nb\n r\n baz",
|
||||
4,
|
||||
},
|
||||
// Trailing whitespace is removed after used for wrapping.
|
||||
// Runs of whitespace on which a line is broken are removed.
|
||||
{
|
||||
"foo \nb ar ",
|
||||
"foo\n\nb\nar\n",
|
||||
4,
|
||||
},
|
||||
// An explicit line break at the end of the input is preserved.
|
||||
{
|
||||
"foo bar baz\n",
|
||||
"foo\nbar\nbaz\n",
|
||||
4,
|
||||
},
|
||||
// Explicit break are always preserved.
|
||||
{
|
||||
"\nfoo bar\n\n\nbaz\n",
|
||||
"\nfoo\nbar\n\n\nbaz\n",
|
||||
4,
|
||||
},
|
||||
// Complete example:
|
||||
{
|
||||
" This is a list: \n\n\t* foo\n\t* bar\n\n\n\t* baz \nBAM ",
|
||||
" This\nis a\nlist:\n\n *\nfoo\n *\nbar\n\n\n *\nbaz\nBAM\n",
|
||||
6,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tc := range cases {
|
||||
actual, lines := TextWrap(tc.Input, tc.Lim)
|
||||
if actual != tc.Output {
|
||||
t.Fatalf("Case %d Input:\n\n`%s`\n\nExpected Output:\n\n`%s`\n\nActual Output:\n\n`%s`",
|
||||
i, tc.Input, tc.Output, actual)
|
||||
}
|
||||
|
||||
expected := len(strings.Split(tc.Output, "\n"))
|
||||
if expected != lines {
|
||||
t.Fatalf("Nb lines mismatch\nExpected:%d\nActual:%d",
|
||||
expected, lines)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user