1
0

simplify indent-only operation

This commit is contained in:
Arpad Ryszka 2025-11-02 01:04:54 +01:00
parent 259163c737
commit f8e59c1465
6 changed files with 442 additions and 368 deletions

183
indent.go
View File

@ -1,181 +1,34 @@
package textedit package textedit
import "unicode" type indentState struct {
started, lineStarted bool
const nonbreakSpace = '\u00a0'
type wrapIndentState struct {
currentWord []rune
currentLineLength int
multipleLines bool
} }
func indentLength(i []rune) int { func indentEdit(first, rest []rune) func(rune, indentState) ([]rune, indentState) {
var l int return func(r rune, s indentState) ([]rune, indentState) {
for _, ii := range i { if r == '\n' {
if ii == '\t' { s.started = true
l += 8 s.lineStarted = false
continue return []rune{'\n'}, s
} }
l++
}
return l
}
func wrapIndentEdit(first, rest []rune, firstWidth, restWidth int) func(rune, wrapIndentState) ([]rune, wrapIndentState) {
firstIndentLength := indentLength(first)
restIndentLength := indentLength(rest)
return func(r rune, state wrapIndentState) ([]rune, wrapIndentState) {
var ret []rune var ret []rune
indent := first if !s.started {
il := firstIndentLength ret = append(ret, first...)
width := firstWidth } else if !s.lineStarted {
if state.multipleLines {
indent = rest
il = restIndentLength
width = restWidth
}
nw := width <= 0
cl := state.currentLineLength
wl := len(state.currentWord)
nl := r == '\n'
ws := unicode.IsSpace(r) && r != nonbreakSpace
if nw && nl && wl > 0 {
if cl == 0 {
ret = append(ret, indent...)
}
if cl > 0 {
ret = append(ret, ' ')
}
ret = append(ret, state.currentWord...)
ret = append(ret, '\n')
state.currentWord = nil
state.currentLineLength = 0
state.multipleLines = true
return ret, state
}
if nw && nl {
ret = append(ret, '\n')
state.currentLineLength = 0
state.multipleLines = true
return ret, state
}
if nw && ws && wl > 0 {
if cl == 0 {
ret = append(ret, indent...)
}
if cl > 0 {
ret = append(ret, ' ')
state.currentLineLength++
}
ret = append(ret, state.currentWord...)
state.currentLineLength += wl
state.currentWord = nil
return ret, state
}
if nw && ws {
return nil, state
}
if nw {
state.currentWord = append(state.currentWord, r)
return nil, state
}
if ws && cl > 0 && cl+wl+il+1 > width && wl > 0 {
ret = append(ret, '\n')
ret = append(ret, rest...) ret = append(ret, rest...)
ret = append(ret, state.currentWord...)
state.currentLineLength = wl
state.multipleLines = true
state.currentWord = nil
return ret, state
} }
if ws && cl > 0 && cl+il+1 > width { ret = append(ret, r)
ret = append(ret, '\n') s.started = true
state.currentLineLength = 0 s.lineStarted = true
state.multipleLines = true return ret, s
return ret, state
}
if ws && cl > 0 && wl > 0 {
ret = append(ret, ' ')
ret = append(ret, state.currentWord...)
state.currentLineLength++
state.currentLineLength += wl
state.currentWord = nil
return ret, state
}
if ws && wl > 0 {
ret = append(ret, indent...)
ret = append(ret, state.currentWord...)
state.currentLineLength += wl
state.currentWord = nil
return ret, state
}
if ws {
return nil, state
}
state.currentWord = append(state.currentWord, r)
return nil, state
} }
} }
func wrapIndentRelease(first, rest []rune, firstWidth, restWidth int) func(wrapIndentState) []rune { func indent(first, rest []rune) Editor {
return func(state wrapIndentState) []rune {
if len(state.currentWord) == 0 {
return nil
}
var ret []rune
indent := first
width := firstWidth
if state.multipleLines {
indent = rest
width = restWidth
}
if width > 0 && state.currentLineLength > 0 &&
state.currentLineLength+len(state.currentWord)+1+len(indent) > width {
ret = append(ret, '\n')
state.currentLineLength = 0
state.multipleLines = true
}
if state.multipleLines {
indent = rest
}
if state.currentLineLength == 0 {
ret = append(ret, indent...)
}
if state.currentLineLength > 0 {
ret = append(ret, ' ')
}
ret = append(ret, state.currentWord...)
return ret
}
}
func wrapIndent(first, rest []rune, firstWidth, restWidth int) Editor {
return Func( return Func(
wrapIndentEdit(first, rest, firstWidth, restWidth), indentEdit(first, rest),
wrapIndentRelease(first, rest, firstWidth, restWidth), func(indentState) []rune { return nil },
) )
} }

View File

@ -57,16 +57,6 @@ func TestIndent(t *testing.T) {
t.Fatal(b.String()) t.Fatal(b.String())
} }
}) })
t.Run("indent last word", func(t *testing.T) {
var b bytes.Buffer
w := textedit.New(&b, textedit.Indent("xxxx", "xxxx"))
w.Write([]byte("foo bar bar foo\n baz\nqux quux\nfoo"))
w.Flush()
if b.String() != "xxxxfoo bar bar foo\nxxxxbaz\nxxxxqux quux\nxxxxfoo" {
t.Fatal(b.String())
}
})
}) })
t.Run("indent no wrap", func(t *testing.T) { t.Run("indent no wrap", func(t *testing.T) {
@ -80,6 +70,16 @@ func TestIndent(t *testing.T) {
} }
}) })
t.Run("indent last word", func(t *testing.T) {
var b bytes.Buffer
w := textedit.New(&b, textedit.Indent("xxxx", "xxxx"))
w.Write([]byte("foo bar bar foo\n baz\nqux quux\nfoo"))
w.Flush()
if b.String() != "xxxxfoo bar bar foo\nxxxx baz\nxxxxqux quux\nxxxxfoo" {
t.Fatal(b.String())
}
})
t.Run("first out", func(t *testing.T) { t.Run("first out", func(t *testing.T) {
var b bytes.Buffer var b bytes.Buffer
w := textedit.New(&b, textedit.Indent("", "xx")) w := textedit.New(&b, textedit.Indent("", "xx"))
@ -120,182 +120,4 @@ func TestIndent(t *testing.T) {
} }
}) })
}) })
t.Run("wrap", func(t *testing.T) {
t.Run("same first and rest", func(t *testing.T) {
var b bytes.Buffer
w := textedit.New(&b, textedit.Wrap(9, 9))
w.Write([]byte("foo bar\n baz\nqux quux\n"))
w.Flush()
if b.String() != "foo bar\nbaz qux\nquux" {
t.Fatal(b.String())
}
})
t.Run("shorter first", func(t *testing.T) {
var b bytes.Buffer
w := textedit.New(&b, textedit.Wrap(5, 9))
w.Write([]byte("foo bar\n baz\nqux quux\n"))
w.Flush()
if b.String() != "foo\nbar baz\nqux quux" {
t.Fatal(b.String())
}
})
t.Run("longer first", func(t *testing.T) {
var b bytes.Buffer
w := textedit.New(&b, textedit.Wrap(9, 5))
w.Write([]byte("foo bar\n baz\nqux quux\n"))
w.Flush()
if b.String() != "foo bar\nbaz\nqux\nquux" {
t.Fatal(b.String())
}
})
t.Run("word longer than width", func(t *testing.T) {
var b bytes.Buffer
w := textedit.New(&b, textedit.Wrap(2, 2))
w.Write([]byte("foo bar\n baz\nqux quux\n"))
w.Flush()
if b.String() != "foo\nbar\nbaz\nqux\nquux" {
t.Fatal(b.String())
}
})
})
t.Run("indent and wrap", func(t *testing.T) {
const text = `
Walking through the mixed forests of Brandenburg in early autumn, one notices the dominant
presence of Scots pine (Pinus sylvestris) interspersed with sessile oak (Quercus petraea) and
silver birch (Betula pendula), their canopies creating a mosaic of light and shadow on the
forest floor. The sandy, acidic soils typical of this region support a ground layer rich in
ericaceous plants, particularly bilberry (Vaccinium myrtillus) with its dark-green oval leaves
now tinged with burgundy, and the occasional patch of heather (Calluna vulgaris) persisting in
sunnier clearings. Closer inspection of the understory reveals common wood sorrel (Oxalis
acetosella) thriving in moister pockets, while various moss speciesincluding the feathery
fronds of Hypnum cupressiformecarpet fallen logs in varying stages of decay. The drier sections
host wavy hair-grass (Deschampsia flexuosa) in delicate tufts, and where old pines have been
felled, pioneering stands of downy birch and rowan (Sorbus aucuparia) compete for space, their
growth marking the forest's continuous cycle of regeneration in this characteristically glacial
landscape of the North European Plain.
`
t.Run("uniform", func(t *testing.T) {
var b bytes.Buffer
w := textedit.New(&b, textedit.WrapIndent(" ", " ", 120, 120))
w.Write([]byte(text))
w.Flush()
const expect = `
Walking through the mixed forests of Brandenburg in early autumn, one notices the dominant presence of Scots pine
(Pinus sylvestris) interspersed with sessile oak (Quercus petraea) and silver birch (Betula pendula), their canopies
creating a mosaic of light and shadow on the forest floor. The sandy, acidic soils typical of this region support a
ground layer rich in ericaceous plants, particularly bilberry (Vaccinium myrtillus) with its dark-green oval leaves
now tinged with burgundy, and the occasional patch of heather (Calluna vulgaris) persisting in sunnier clearings.
Closer inspection of the understory reveals common wood sorrel (Oxalis acetosella) thriving in moister pockets,
while various moss speciesincluding the feathery fronds of Hypnum cupressiformecarpet fallen logs in varying
stages of decay. The drier sections host wavy hair-grass (Deschampsia flexuosa) in delicate tufts, and where old
pines have been felled, pioneering stands of downy birch and rowan (Sorbus aucuparia) compete for space, their
growth marking the forest's continuous cycle of regeneration in this characteristically glacial landscape of the
North European Plain.`
if "\n"+b.String() != expect {
t.Fatal("\n" + b.String())
}
})
t.Run("classic", func(t *testing.T) {
var b bytes.Buffer
w := textedit.New(&b, textedit.WrapIndent(" ", "", 120, 120))
w.Write([]byte(text))
w.Flush()
const expect = `
Walking through the mixed forests of Brandenburg in early autumn, one notices the dominant presence of Scots pine
(Pinus sylvestris) interspersed with sessile oak (Quercus petraea) and silver birch (Betula pendula), their canopies
creating a mosaic of light and shadow on the forest floor. The sandy, acidic soils typical of this region support a
ground layer rich in ericaceous plants, particularly bilberry (Vaccinium myrtillus) with its dark-green oval leaves now
tinged with burgundy, and the occasional patch of heather (Calluna vulgaris) persisting in sunnier clearings. Closer
inspection of the understory reveals common wood sorrel (Oxalis acetosella) thriving in moister pockets, while various
moss speciesincluding the feathery fronds of Hypnum cupressiformecarpet fallen logs in varying stages of decay. The
drier sections host wavy hair-grass (Deschampsia flexuosa) in delicate tufts, and where old pines have been felled,
pioneering stands of downy birch and rowan (Sorbus aucuparia) compete for space, their growth marking the forest's
continuous cycle of regeneration in this characteristically glacial landscape of the North European Plain.`
if "\n"+b.String() != expect {
t.Fatal("\n" + b.String())
}
})
t.Run("indent out", func(t *testing.T) {
var b bytes.Buffer
w := textedit.New(&b, textedit.WrapIndent("", " ", 120, 120))
w.Write([]byte(text))
w.Flush()
const expect = `
Walking through the mixed forests of Brandenburg in early autumn, one notices the dominant presence of Scots pine (Pinus
sylvestris) interspersed with sessile oak (Quercus petraea) and silver birch (Betula pendula), their canopies
creating a mosaic of light and shadow on the forest floor. The sandy, acidic soils typical of this region support a
ground layer rich in ericaceous plants, particularly bilberry (Vaccinium myrtillus) with its dark-green oval leaves
now tinged with burgundy, and the occasional patch of heather (Calluna vulgaris) persisting in sunnier clearings.
Closer inspection of the understory reveals common wood sorrel (Oxalis acetosella) thriving in moister pockets,
while various moss speciesincluding the feathery fronds of Hypnum cupressiformecarpet fallen logs in varying
stages of decay. The drier sections host wavy hair-grass (Deschampsia flexuosa) in delicate tufts, and where old
pines have been felled, pioneering stands of downy birch and rowan (Sorbus aucuparia) compete for space, their
growth marking the forest's continuous cycle of regeneration in this characteristically glacial landscape of the
North European Plain.`
if "\n"+b.String() != expect {
t.Fatal("\n" + b.String())
}
})
t.Run("indent out with same width", func(t *testing.T) {
var b bytes.Buffer
w := textedit.New(&b, textedit.WrapIndent("", " ", 116, 120))
w.Write([]byte(text))
w.Flush()
const expect = `
Walking through the mixed forests of Brandenburg in early autumn, one notices the dominant presence of Scots pine
(Pinus sylvestris) interspersed with sessile oak (Quercus petraea) and silver birch (Betula pendula), their canopies
creating a mosaic of light and shadow on the forest floor. The sandy, acidic soils typical of this region support a
ground layer rich in ericaceous plants, particularly bilberry (Vaccinium myrtillus) with its dark-green oval leaves
now tinged with burgundy, and the occasional patch of heather (Calluna vulgaris) persisting in sunnier clearings.
Closer inspection of the understory reveals common wood sorrel (Oxalis acetosella) thriving in moister pockets,
while various moss speciesincluding the feathery fronds of Hypnum cupressiformecarpet fallen logs in varying
stages of decay. The drier sections host wavy hair-grass (Deschampsia flexuosa) in delicate tufts, and where old
pines have been felled, pioneering stands of downy birch and rowan (Sorbus aucuparia) compete for space, their
growth marking the forest's continuous cycle of regeneration in this characteristically glacial landscape of the
North European Plain.`
if "\n"+b.String() != expect {
t.Fatal("\n" + b.String())
}
})
t.Run("tab is 8", func(t *testing.T) {
var b bytes.Buffer
w := textedit.New(&b, textedit.WrapIndent("\t", "", 12, 12))
w.Write([]byte("foo bar\n baz\nqux quux\n"))
w.Flush()
if b.String() != "\tfoo\nbar baz qux\nquux" {
t.Fatal(b.String())
}
})
t.Run("wrap on release", func(t *testing.T) {
var b bytes.Buffer
w := textedit.New(&b, textedit.WrapIndent(" ", " ", 15, 15))
w.Write([]byte("Some sample text...\n on multiple lines."))
w.Flush()
const expect = `
Some sample
text... on
multiple
lines.`
if "\n"+b.String() != expect {
t.Fatal("\n" + b.String())
}
})
})
} }

10
lib.go
View File

@ -72,10 +72,9 @@ func Escape(esc rune, chars ...rune) Editor {
return escape(esc, chars...) return escape(esc, chars...)
} }
// Indent applies indentation to multi-line text. Duplicate, leading, trailing and non-space whitespaces, other // Indent applies indentation to multi-line text. Whitespaces are preserved.
// than the newline character, are collapsed into a single space.
func Indent(first, rest string) Editor { func Indent(first, rest string) Editor {
return wrapIndent([]rune(first), []rune(rest), 0, 0) return indent([]rune(first), []rune(rest))
} }
// Wrap wrap wraps multiline text to define maximual text width. Duplicate, leading, trailing and non-space // Wrap wrap wraps multiline text to define maximual text width. Duplicate, leading, trailing and non-space
@ -91,10 +90,7 @@ func WrapIndent(firstIndent, restIndent string, firstWidth, restWidth int) Edito
// SingleLine is like Wrap, but with infinite text width. // SingleLine is like Wrap, but with infinite text width.
func SingleLine() Editor { func SingleLine() Editor {
return sequence( return singleLine()
replace("\n", " "),
wrapIndent(nil, nil, 0, 0),
)
} }
// New initializes an editing writer. The editor instances will be called in the order they are passed in to // New initializes an editing writer. The editor instances will be called in the order they are passed in to

36
singleline.go Normal file
View File

@ -0,0 +1,36 @@
package textedit
import "unicode"
type singleLineState struct {
started bool
currentWord []rune
}
func singleLineEdit(r rune, s singleLineState) ([]rune, singleLineState) {
if !unicode.IsSpace(r) || r == nonbreakSpace {
s.currentWord = append(s.currentWord, r)
return nil, s
}
if len(s.currentWord) == 0 {
return nil, s
}
ret := s.currentWord
if s.started {
ret = append([]rune{' '}, ret...)
}
s.currentWord = nil
s.started = true
return ret, s
}
func singleLineReleaseState(s singleLineState) []rune {
return s.currentWord
}
func singleLine() Editor {
return Func(singleLineEdit, singleLineReleaseState)
}

130
wrap.go Normal file
View File

@ -0,0 +1,130 @@
package textedit
import "unicode"
const nonbreakSpace = '\u00a0'
type wrapIndentState struct {
currentWord []rune
currentLineLength int
multipleLines bool
}
func indentLength(i []rune) int {
var l int
for _, ii := range i {
if ii == '\t' {
l += 8
continue
}
l++
}
return l
}
func wrapIndentEdit(first, rest []rune, firstWidth, restWidth int) func(rune, wrapIndentState) ([]rune, wrapIndentState) {
firstIndentLength := indentLength(first)
restIndentLength := indentLength(rest)
return func(r rune, state wrapIndentState) ([]rune, wrapIndentState) {
var ret []rune
indent := first
il := firstIndentLength
width := firstWidth
if state.multipleLines {
indent = rest
il = restIndentLength
width = restWidth
}
cl := state.currentLineLength
wl := len(state.currentWord)
ws := unicode.IsSpace(r) && r != nonbreakSpace
if ws && cl > 0 && cl+wl+il+1 > width && wl > 0 {
ret = append(ret, '\n')
ret = append(ret, rest...)
ret = append(ret, state.currentWord...)
state.currentLineLength = wl
state.multipleLines = true
state.currentWord = nil
return ret, state
}
if ws && cl > 0 && cl+il+1 > width {
ret = append(ret, '\n')
state.currentLineLength = 0
state.multipleLines = true
return ret, state
}
if ws && cl > 0 && wl > 0 {
ret = append(ret, ' ')
ret = append(ret, state.currentWord...)
state.currentLineLength++
state.currentLineLength += wl
state.currentWord = nil
return ret, state
}
if ws && wl > 0 {
ret = append(ret, indent...)
ret = append(ret, state.currentWord...)
state.currentLineLength += wl
state.currentWord = nil
return ret, state
}
if ws {
return nil, state
}
state.currentWord = append(state.currentWord, r)
return nil, state
}
}
func wrapIndentRelease(first, rest []rune, firstWidth, restWidth int) func(wrapIndentState) []rune {
return func(state wrapIndentState) []rune {
if len(state.currentWord) == 0 {
return nil
}
var ret []rune
indent := first
width := firstWidth
if state.multipleLines {
indent = rest
width = restWidth
}
if state.currentLineLength > 0 &&
state.currentLineLength+len(state.currentWord)+1+len(indent) > width {
ret = append(ret, '\n')
state.currentLineLength = 0
state.multipleLines = true
}
if state.multipleLines {
indent = rest
}
if state.currentLineLength == 0 {
ret = append(ret, indent...)
}
if state.currentLineLength > 0 {
ret = append(ret, ' ')
}
ret = append(ret, state.currentWord...)
return ret
}
}
func wrapIndent(first, rest []rune, firstWidth, restWidth int) Editor {
return Func(
wrapIndentEdit(first, rest, firstWidth, restWidth),
wrapIndentRelease(first, rest, firstWidth, restWidth),
)
}

237
wrap_test.go Normal file
View File

@ -0,0 +1,237 @@
package textedit_test
import (
"bytes"
"code.squareroundforest.org/arpio/textedit"
"testing"
)
func TestWrap(t *testing.T) {
t.Run("wrap", func(t *testing.T) {
t.Run("same first and rest", func(t *testing.T) {
var b bytes.Buffer
w := textedit.New(&b, textedit.Wrap(9, 9))
w.Write([]byte("foo bar\n baz\nqux quux\n"))
w.Flush()
if b.String() != "foo bar\nbaz qux\nquux" {
t.Fatal(b.String())
}
})
t.Run("shorter first", func(t *testing.T) {
var b bytes.Buffer
w := textedit.New(&b, textedit.Wrap(5, 9))
w.Write([]byte("foo bar\n baz\nqux quux\n"))
w.Flush()
if b.String() != "foo\nbar baz\nqux quux" {
t.Fatal(b.String())
}
})
t.Run("longer first", func(t *testing.T) {
var b bytes.Buffer
w := textedit.New(&b, textedit.Wrap(9, 5))
w.Write([]byte("foo bar\n baz\nqux quux\n"))
w.Flush()
if b.String() != "foo bar\nbaz\nqux\nquux" {
t.Fatal(b.String())
}
})
t.Run("word longer than width", func(t *testing.T) {
var b bytes.Buffer
w := textedit.New(&b, textedit.Wrap(2, 2))
w.Write([]byte("foo bar\n baz\nqux quux\n"))
w.Flush()
if b.String() != "foo\nbar\nbaz\nqux\nquux" {
t.Fatal(b.String())
}
})
t.Run("zero width", func(t *testing.T) {
var b bytes.Buffer
w := textedit.New(&b, textedit.Wrap(0, 0))
w.Write([]byte("foo bar\n baz\nqux quux\n"))
w.Flush()
if b.String() != "foo\nbar\nbaz\nqux\nquux" {
t.Fatal(b.String())
}
})
t.Run("negative width", func(t *testing.T) {
var b bytes.Buffer
w := textedit.New(&b, textedit.Wrap(-2, -2))
w.Write([]byte("foo bar\n baz\nqux quux\n"))
w.Flush()
if b.String() != "foo\nbar\nbaz\nqux\nquux" {
t.Fatal(b.String())
}
})
})
t.Run("indent and wrap", func(t *testing.T) {
const text = `
Walking through the mixed forests of Brandenburg in early autumn, one notices the dominant
presence of Scots pine (Pinus sylvestris) interspersed with sessile oak (Quercus petraea) and
silver birch (Betula pendula), their canopies creating a mosaic of light and shadow on the
forest floor. The sandy, acidic soils typical of this region support a ground layer rich in
ericaceous plants, particularly bilberry (Vaccinium myrtillus) with its dark-green oval leaves
now tinged with burgundy, and the occasional patch of heather (Calluna vulgaris) persisting in
sunnier clearings. Closer inspection of the understory reveals common wood sorrel (Oxalis
acetosella) thriving in moister pockets, while various moss speciesincluding the feathery
fronds of Hypnum cupressiformecarpet fallen logs in varying stages of decay. The drier sections
host wavy hair-grass (Deschampsia flexuosa) in delicate tufts, and where old pines have been
felled, pioneering stands of downy birch and rowan (Sorbus aucuparia) compete for space, their
growth marking the forest's continuous cycle of regeneration in this characteristically glacial
landscape of the North European Plain.
`
t.Run("uniform", func(t *testing.T) {
var b bytes.Buffer
w := textedit.New(&b, textedit.WrapIndent(" ", " ", 120, 120))
w.Write([]byte(text))
w.Flush()
const expect = `
Walking through the mixed forests of Brandenburg in early autumn, one notices the dominant presence of Scots pine
(Pinus sylvestris) interspersed with sessile oak (Quercus petraea) and silver birch (Betula pendula), their canopies
creating a mosaic of light and shadow on the forest floor. The sandy, acidic soils typical of this region support a
ground layer rich in ericaceous plants, particularly bilberry (Vaccinium myrtillus) with its dark-green oval leaves
now tinged with burgundy, and the occasional patch of heather (Calluna vulgaris) persisting in sunnier clearings.
Closer inspection of the understory reveals common wood sorrel (Oxalis acetosella) thriving in moister pockets,
while various moss speciesincluding the feathery fronds of Hypnum cupressiformecarpet fallen logs in varying
stages of decay. The drier sections host wavy hair-grass (Deschampsia flexuosa) in delicate tufts, and where old
pines have been felled, pioneering stands of downy birch and rowan (Sorbus aucuparia) compete for space, their
growth marking the forest's continuous cycle of regeneration in this characteristically glacial landscape of the
North European Plain.`
if "\n"+b.String() != expect {
t.Fatal("\n" + b.String())
}
})
t.Run("classic", func(t *testing.T) {
var b bytes.Buffer
w := textedit.New(&b, textedit.WrapIndent(" ", "", 120, 120))
w.Write([]byte(text))
w.Flush()
const expect = `
Walking through the mixed forests of Brandenburg in early autumn, one notices the dominant presence of Scots pine
(Pinus sylvestris) interspersed with sessile oak (Quercus petraea) and silver birch (Betula pendula), their canopies
creating a mosaic of light and shadow on the forest floor. The sandy, acidic soils typical of this region support a
ground layer rich in ericaceous plants, particularly bilberry (Vaccinium myrtillus) with its dark-green oval leaves now
tinged with burgundy, and the occasional patch of heather (Calluna vulgaris) persisting in sunnier clearings. Closer
inspection of the understory reveals common wood sorrel (Oxalis acetosella) thriving in moister pockets, while various
moss speciesincluding the feathery fronds of Hypnum cupressiformecarpet fallen logs in varying stages of decay. The
drier sections host wavy hair-grass (Deschampsia flexuosa) in delicate tufts, and where old pines have been felled,
pioneering stands of downy birch and rowan (Sorbus aucuparia) compete for space, their growth marking the forest's
continuous cycle of regeneration in this characteristically glacial landscape of the North European Plain.`
if "\n"+b.String() != expect {
t.Fatal("\n" + b.String())
}
})
t.Run("indent out", func(t *testing.T) {
var b bytes.Buffer
w := textedit.New(&b, textedit.WrapIndent("", " ", 120, 120))
w.Write([]byte(text))
w.Flush()
const expect = `
Walking through the mixed forests of Brandenburg in early autumn, one notices the dominant presence of Scots pine (Pinus
sylvestris) interspersed with sessile oak (Quercus petraea) and silver birch (Betula pendula), their canopies
creating a mosaic of light and shadow on the forest floor. The sandy, acidic soils typical of this region support a
ground layer rich in ericaceous plants, particularly bilberry (Vaccinium myrtillus) with its dark-green oval leaves
now tinged with burgundy, and the occasional patch of heather (Calluna vulgaris) persisting in sunnier clearings.
Closer inspection of the understory reveals common wood sorrel (Oxalis acetosella) thriving in moister pockets,
while various moss speciesincluding the feathery fronds of Hypnum cupressiformecarpet fallen logs in varying
stages of decay. The drier sections host wavy hair-grass (Deschampsia flexuosa) in delicate tufts, and where old
pines have been felled, pioneering stands of downy birch and rowan (Sorbus aucuparia) compete for space, their
growth marking the forest's continuous cycle of regeneration in this characteristically glacial landscape of the
North European Plain.`
if "\n"+b.String() != expect {
t.Fatal("\n" + b.String())
}
})
t.Run("indent out with same width", func(t *testing.T) {
var b bytes.Buffer
w := textedit.New(&b, textedit.WrapIndent("", " ", 116, 120))
w.Write([]byte(text))
w.Flush()
const expect = `
Walking through the mixed forests of Brandenburg in early autumn, one notices the dominant presence of Scots pine
(Pinus sylvestris) interspersed with sessile oak (Quercus petraea) and silver birch (Betula pendula), their canopies
creating a mosaic of light and shadow on the forest floor. The sandy, acidic soils typical of this region support a
ground layer rich in ericaceous plants, particularly bilberry (Vaccinium myrtillus) with its dark-green oval leaves
now tinged with burgundy, and the occasional patch of heather (Calluna vulgaris) persisting in sunnier clearings.
Closer inspection of the understory reveals common wood sorrel (Oxalis acetosella) thriving in moister pockets,
while various moss speciesincluding the feathery fronds of Hypnum cupressiformecarpet fallen logs in varying
stages of decay. The drier sections host wavy hair-grass (Deschampsia flexuosa) in delicate tufts, and where old
pines have been felled, pioneering stands of downy birch and rowan (Sorbus aucuparia) compete for space, their
growth marking the forest's continuous cycle of regeneration in this characteristically glacial landscape of the
North European Plain.`
if "\n"+b.String() != expect {
t.Fatal("\n" + b.String())
}
})
t.Run("tab is 8", func(t *testing.T) {
var b bytes.Buffer
w := textedit.New(&b, textedit.WrapIndent("\t", "", 12, 12))
w.Write([]byte("foo bar\n baz\nqux quux\n"))
w.Flush()
if b.String() != "\tfoo\nbar baz qux\nquux" {
t.Fatal(b.String())
}
})
t.Run("wrap on release", func(t *testing.T) {
var b bytes.Buffer
w := textedit.New(&b, textedit.WrapIndent(" ", " ", 15, 15))
w.Write([]byte("Some sample text...\n on multiple lines."))
w.Flush()
const expect = `
Some sample
text... on
multiple
lines.`
if "\n"+b.String() != expect {
t.Fatal("\n" + b.String())
}
})
t.Run("zero width", func(t *testing.T) {
var b bytes.Buffer
w := textedit.New(&b, textedit.WrapIndent("xx", "xx", 0, 0))
w.Write([]byte("foo bar\n baz\nqux quux\n"))
w.Flush()
if b.String() != "xxfoo\nxxbar\nxxbaz\nxxqux\nxxquux" {
t.Fatal(b.String())
}
})
t.Run("negative width", func(t *testing.T) {
var b bytes.Buffer
w := textedit.New(&b, textedit.WrapIndent("xx", "xx", -2, -2))
w.Write([]byte("foo bar\n baz\nqux quux\n"))
w.Flush()
if b.String() != "xxfoo\nxxbar\nxxbaz\nxxqux\nxxquux" {
t.Fatal(b.String())
}
})
t.Run("last word on the same line", func(t *testing.T) {
var b bytes.Buffer
w := textedit.New(&b, textedit.WrapIndent("xx", "xx", 9, 9))
w.Write([]byte("foo bar"))
w.Flush()
if b.String() != "xxfoo bar" {
t.Fatal(b.String())
}
})
})
}