gfishline/splitter.go

162 lines
3.3 KiB
Go

package gfishline
import (
"strings"
"gitea.zaclys.com/bvaudour/gob/shell/scanner"
)
type splitter struct {
space map[int]string
words []string
cursor int
}
func newSplitter(buffer string, cursor int) *splitter {
sp := splitter{
space: make(map[int]string),
cursor: cursor,
}
sc := scanner.NewRawScanner(strings.NewReader(buffer))
for sc.Scan() {
sp.words = append(sp.words, sc.Text())
}
for i, word := range sp.words {
if strings.HasPrefix(buffer, word) {
buffer = strings.TrimPrefix(buffer, word)
} else {
tmp := strings.TrimLeft(buffer, " ")
sp.space[i] = strings.Repeat(" ", len(buffer)-len(tmp))
buffer = strings.TrimPrefix(tmp, word)
}
}
if len(buffer) > 0 {
l := len(sp.words)
tmp := strings.TrimPrefix(buffer, " ")
if len(tmp) < len(buffer) {
sp.space[l] = strings.Repeat(" ", len(buffer)-len(tmp))
buffer = tmp
l++
}
if len(buffer) > 0 {
var lastWord, lastSpace []rune
for _, r := range buffer {
if r == ' ' {
lastSpace = append(lastSpace, r)
} else {
if len(lastWord) == 0 {
lastWord, lastSpace = append(lastWord, lastSpace...), lastSpace[:0]
}
lastWord = append(lastWord, r)
}
}
sp.words = append(sp.words, string(lastWord))
if len(lastSpace) > 0 {
sp.space[l] = string(lastSpace)
}
}
}
return &sp
}
func (sp *splitter) isComplete() (ok bool) {
l := len(sp.words)
if ok = l == 0; !ok {
sc := scanner.NewScanner(strings.NewReader(sp.words[l-1]))
ok = sc.Scan()
}
return
}
func (sp *splitter) join() string {
var buf strings.Builder
for i, word := range sp.words {
if space, ok := sp.space[i]; ok {
buf.WriteString(space)
}
buf.WriteString(word)
}
if space, ok := sp.space[len(sp.words)]; ok {
buf.WriteString(space)
}
return buf.String()
}
func (sp *splitter) buffer() (buffer string, cursor int) { return sp.join(), sp.cursor }
func (sp *splitter) setWord(index int, word string) {
delta := len([]rune(word)) - len([]rune(sp.words[index]))
sp.words[index] = word
sp.cursor += delta
}
func (sp *splitter) addWord(index int, word string, wordPos int) {
delta, l := len([]rune(word))+2, len(sp.words)
words := make([]string, l+1)
copy(words[:index], sp.words[:index])
copy(words[index+1:], sp.words[index:])
words[index] = word
space := make(map[int]string)
for i, s := range sp.space {
switch {
case i < index:
space[i] = s
case i > index:
space[i+1] = s
default:
space[i] = s[:wordPos]
space[i+1] = " " + s[wordPos:]
}
}
if _, ok := sp.space[index+1]; !ok {
sp.space[index+1] = " "
}
sp.words, sp.space = words, space
sp.cursor += delta
}
func (sp *splitter) compute() (wordIndex, wordCursor int, isIndexSpace bool) {
if isIndexSpace = len(sp.words) == 0 && len(sp.space) == 0; isIndexSpace {
return
}
i := 0
for wi, word := range sp.words {
if space, ok := sp.space[i]; ok {
l := len(space)
i += l
if isIndexSpace = i > sp.cursor; isIndexSpace {
wordIndex, wordCursor = wi, sp.cursor-(i-l)
return
}
}
l := len([]rune(word))
i += l
if i > sp.cursor {
wordIndex, wordCursor, isIndexSpace = wi, sp.cursor-(i-l), false
return
}
}
l := len(sp.words)
wordCursor = sp.cursor - i
if _, isIndexSpace = sp.space[l]; isIndexSpace {
wordIndex = l
} else {
wordIndex = l - 1
}
return
}