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 }