mirror of
https://github.com/MichaelMure/git-bug.git
synced 2024-12-16 02:33:26 +03:00
util: add a text wrapping function
This commit is contained in:
parent
d88d59e9c5
commit
5c86164f22
@ -2,8 +2,8 @@ package termui
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/MichaelMure/git-bug/util"
|
||||||
"github.com/jroimartin/gocui"
|
"github.com/jroimartin/gocui"
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const errorPopupView = "errorPopupView"
|
const errorPopupView = "errorPopupView"
|
||||||
@ -37,7 +37,7 @@ func (ep *errorPopup) layout(g *gocui.Gui) error {
|
|||||||
maxX, maxY := g.Size()
|
maxX, maxY := g.Size()
|
||||||
|
|
||||||
width := minInt(30, maxX)
|
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)
|
height := minInt(nblines+2, maxY)
|
||||||
x0 := (maxX - width) / 2
|
x0 := (maxX - width) / 2
|
||||||
y0 := (maxY - height) / 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) {
|
func (ep *errorPopup) activate(message string) {
|
||||||
ep.message = message
|
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