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), ) }