aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--termui/error_popup.go26
-rw-r--r--util/text.go100
-rw-r--r--util/text_test.go89
3 files changed, 191 insertions, 24 deletions
diff --git a/termui/error_popup.go b/termui/error_popup.go
index e46a00e7..855bd05d 100644
--- a/termui/error_popup.go
+++ b/termui/error_popup.go
@@ -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
-}
diff --git a/util/text.go b/util/text.go
new file mode 100644
index 00000000..83a101d3
--- /dev/null
+++ b/util/text.go
@@ -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
+}
diff --git a/util/text_test.go b/util/text_test.go
new file mode 100644
index 00000000..f7e1fabf
--- /dev/null
+++ b/util/text_test.go
@@ -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)
+ }
+ }
+}