1
0
textedit/indent.go
2025-11-01 03:49:02 +01:00

169 lines
3.3 KiB
Go

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
}
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, state.currentWord...)
state.currentLineLength = wl
state.multipleLines = true
state.currentWord = nil
return ret, state
}
if ws && cl > 0 && cl+wl+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) func(wrapIndentState) []rune {
return func(state wrapIndentState) []rune {
if len(state.currentWord) == 0 {
return nil
}
var ret []rune
indent := first
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),
)
}