162 lines
3.3 KiB
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
|
|
}
|