2023-01-18 06:19:10 +00:00
|
|
|
package textmanip
|
|
|
|
|
|
|
|
import "unicode"
|
|
|
|
|
2023-02-13 06:52:31 +00:00
|
|
|
type Dot struct { Start, End int }
|
|
|
|
|
|
|
|
func EmptyDot (position int) Dot {
|
|
|
|
return Dot { position, position }
|
|
|
|
}
|
|
|
|
|
|
|
|
func (dot Dot) Canon () Dot {
|
|
|
|
if dot.Start > dot.End {
|
|
|
|
return Dot { dot.End, dot.Start }
|
|
|
|
} else {
|
|
|
|
return dot
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (dot Dot) Empty () bool {
|
|
|
|
return dot.Start == dot.End
|
|
|
|
}
|
|
|
|
|
|
|
|
func (dot Dot) Add (delta int) Dot {
|
|
|
|
return Dot {
|
|
|
|
dot.Start + delta,
|
|
|
|
dot.End + delta,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (dot Dot) Sub (delta int) Dot {
|
|
|
|
return Dot {
|
|
|
|
dot.Start - delta,
|
|
|
|
dot.End - delta,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-13 17:55:51 +00:00
|
|
|
func (dot Dot) Constrain (length int) Dot {
|
|
|
|
if dot.Start < 0 { dot.Start = 0 }
|
|
|
|
if dot.Start > length { dot.Start = length}
|
|
|
|
if dot.End < 0 { dot.End = 0 }
|
|
|
|
if dot.End > length { dot.End = length}
|
|
|
|
return dot
|
|
|
|
}
|
|
|
|
|
2023-03-31 01:33:49 +00:00
|
|
|
func (dot Dot) Width () int {
|
|
|
|
dot = dot.Canon()
|
|
|
|
return dot.End - dot.Start
|
|
|
|
}
|
|
|
|
|
|
|
|
func (dot Dot) Slice (text []rune) []rune {
|
|
|
|
return text[dot.Start:dot.End]
|
|
|
|
}
|
|
|
|
|
2023-02-13 17:55:51 +00:00
|
|
|
func WordToLeft (text []rune, position int) (length int) {
|
|
|
|
if position < 1 { return }
|
|
|
|
if position > len(text) { position = len(text) }
|
2023-01-18 06:19:10 +00:00
|
|
|
|
2023-02-13 17:55:51 +00:00
|
|
|
index := position - 1
|
2023-01-18 06:19:10 +00:00
|
|
|
for index >= 0 && unicode.IsSpace(text[index]) {
|
|
|
|
length ++
|
|
|
|
index --
|
|
|
|
}
|
|
|
|
for index >= 0 && !unicode.IsSpace(text[index]) {
|
|
|
|
length ++
|
|
|
|
index --
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2023-02-13 17:55:51 +00:00
|
|
|
func WordToRight (text []rune, position int) (length int) {
|
|
|
|
if position < 0 { return }
|
|
|
|
if position > len(text) { position = len(text) }
|
2023-01-18 06:19:10 +00:00
|
|
|
|
2023-02-13 17:55:51 +00:00
|
|
|
index := position
|
2023-01-18 06:19:10 +00:00
|
|
|
for index < len(text) && unicode.IsSpace(text[index]) {
|
|
|
|
length ++
|
|
|
|
index ++
|
|
|
|
}
|
|
|
|
for index < len(text) && !unicode.IsSpace(text[index]) {
|
|
|
|
length ++
|
|
|
|
index ++
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2023-02-13 17:55:51 +00:00
|
|
|
func WordAround (text []rune, position int) (around Dot) {
|
|
|
|
return Dot {
|
|
|
|
WordToLeft(text, position),
|
|
|
|
WordToRight(text, position),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-13 06:52:31 +00:00
|
|
|
func Backspace (text []rune, dot Dot, word bool) (result []rune, moved Dot) {
|
2023-02-13 17:55:51 +00:00
|
|
|
dot = dot.Constrain(len(text))
|
2023-02-13 06:52:31 +00:00
|
|
|
if dot.Empty() {
|
|
|
|
distance := 1
|
|
|
|
if word {
|
2023-02-13 17:55:51 +00:00
|
|
|
distance = WordToLeft(text, dot.End)
|
2023-02-13 06:52:31 +00:00
|
|
|
}
|
2023-02-13 17:55:51 +00:00
|
|
|
result = append (
|
|
|
|
result,
|
|
|
|
text[:dot.Sub(distance).Constrain(len(text)).End]...)
|
|
|
|
result = append(result, text[dot.End:]...)
|
|
|
|
moved = EmptyDot(dot.Sub(distance).Start)
|
|
|
|
return
|
2023-02-13 06:52:31 +00:00
|
|
|
} else {
|
|
|
|
return Delete(text, dot, word)
|
2023-01-18 06:19:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-13 06:52:31 +00:00
|
|
|
func Delete (text []rune, dot Dot, word bool) (result []rune, moved Dot) {
|
2023-02-13 17:55:51 +00:00
|
|
|
dot = dot.Constrain(len(text))
|
2023-02-13 06:52:31 +00:00
|
|
|
if dot.Empty() {
|
|
|
|
distance := 1
|
|
|
|
if word {
|
2023-02-13 17:55:51 +00:00
|
|
|
distance = WordToRight(text, dot.End)
|
2023-02-13 06:52:31 +00:00
|
|
|
}
|
2023-02-13 17:55:51 +00:00
|
|
|
result = append(result, text[:dot.End]...)
|
|
|
|
result = append (
|
|
|
|
result,
|
|
|
|
text[dot.Add(distance).Constrain(len(text)).End:]...)
|
2023-02-13 06:52:31 +00:00
|
|
|
moved = dot
|
|
|
|
return
|
|
|
|
} else {
|
2023-02-13 17:55:51 +00:00
|
|
|
dot = dot.Canon()
|
2023-02-13 06:52:31 +00:00
|
|
|
result = append(result, text[:dot.Start]...)
|
|
|
|
result = append(result, text[dot.End:]...)
|
|
|
|
moved = EmptyDot(dot.Start)
|
|
|
|
return
|
2023-01-18 06:19:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-31 01:33:49 +00:00
|
|
|
func Lift (text []rune, dot Dot) (result []rune, moved Dot, lifted []rune) {
|
|
|
|
dot = dot.Constrain(len(text))
|
|
|
|
if dot.Empty() {
|
|
|
|
moved = dot
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
dot = dot.Canon()
|
|
|
|
lifted = make([]rune, dot.Width())
|
|
|
|
copy(lifted, dot.Slice(text))
|
|
|
|
result = append(result, text[:dot.Start]...)
|
|
|
|
result = append(result, text[dot.End:]...)
|
|
|
|
moved = EmptyDot(dot.Start)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2023-02-13 06:52:31 +00:00
|
|
|
func Type (text []rune, dot Dot, character rune) (result []rune, moved Dot) {
|
2023-02-13 17:55:51 +00:00
|
|
|
dot = dot.Constrain(len(text))
|
2023-02-13 06:52:31 +00:00
|
|
|
if dot.Empty() {
|
2023-02-13 17:55:51 +00:00
|
|
|
result = append(result, text[:dot.End]...)
|
2023-02-13 06:52:31 +00:00
|
|
|
result = append(result, character)
|
2023-02-13 17:55:51 +00:00
|
|
|
if dot.End < len(text) {
|
|
|
|
result = append(result, text[dot.End:]...)
|
2023-02-13 06:52:31 +00:00
|
|
|
}
|
2023-02-13 17:55:51 +00:00
|
|
|
moved = EmptyDot(dot.Add(1).End)
|
2023-02-13 06:52:31 +00:00
|
|
|
return
|
|
|
|
} else {
|
2023-02-13 17:55:51 +00:00
|
|
|
dot = dot.Canon()
|
2023-02-13 06:52:31 +00:00
|
|
|
result = append(result, text[:dot.Start]...)
|
|
|
|
result = append(result, character)
|
|
|
|
result = append(result, text[dot.End:]...)
|
2023-02-13 17:55:51 +00:00
|
|
|
moved = EmptyDot(dot.Add(1).Start)
|
2023-02-13 06:52:31 +00:00
|
|
|
return
|
2023-01-18 06:19:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-13 06:52:31 +00:00
|
|
|
func MoveLeft (text []rune, dot Dot, word bool) (moved Dot) {
|
2023-02-13 20:26:21 +00:00
|
|
|
dot = dot.Canon().Constrain(len(text))
|
|
|
|
distance := 0
|
|
|
|
if dot.Empty() {
|
|
|
|
distance = 1
|
|
|
|
}
|
2023-01-18 06:19:10 +00:00
|
|
|
if word {
|
2023-02-13 17:55:51 +00:00
|
|
|
distance = WordToLeft(text, dot.Start)
|
2023-01-18 06:19:10 +00:00
|
|
|
}
|
2023-02-13 17:55:51 +00:00
|
|
|
moved = EmptyDot(dot.Sub(distance).Start)
|
2023-01-18 06:19:10 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2023-02-13 06:52:31 +00:00
|
|
|
func MoveRight (text []rune, dot Dot, word bool) (moved Dot) {
|
2023-02-13 20:26:21 +00:00
|
|
|
dot = dot.Canon().Constrain(len(text))
|
|
|
|
distance := 0
|
|
|
|
if dot.Empty() {
|
|
|
|
distance = 1
|
|
|
|
}
|
2023-01-18 06:19:10 +00:00
|
|
|
if word {
|
2023-02-13 17:55:51 +00:00
|
|
|
distance = WordToRight(text, dot.End)
|
2023-01-18 06:19:10 +00:00
|
|
|
}
|
2023-02-13 17:55:51 +00:00
|
|
|
moved = EmptyDot(dot.Add(distance).End)
|
2023-01-18 06:19:10 +00:00
|
|
|
return
|
|
|
|
}
|
2023-02-13 17:55:51 +00:00
|
|
|
|
|
|
|
func SelectLeft (text []rune, dot Dot, word bool) (moved Dot) {
|
|
|
|
dot = dot.Constrain(len(text))
|
|
|
|
distance := 1
|
|
|
|
if word {
|
2023-02-16 19:09:23 +00:00
|
|
|
distance = WordToLeft(text, dot.End)
|
2023-02-13 17:55:51 +00:00
|
|
|
}
|
2023-02-16 19:09:23 +00:00
|
|
|
dot.End -= distance
|
2023-02-13 17:55:51 +00:00
|
|
|
return dot
|
|
|
|
}
|
|
|
|
|
|
|
|
func SelectRight (text []rune, dot Dot, word bool) (moved Dot) {
|
|
|
|
dot = dot.Constrain(len(text))
|
|
|
|
distance := 1
|
|
|
|
if word {
|
|
|
|
distance = WordToRight(text, dot.End)
|
|
|
|
}
|
2023-02-16 19:09:23 +00:00
|
|
|
dot.End += distance
|
2023-02-13 17:55:51 +00:00
|
|
|
return dot
|
|
|
|
}
|