readline/search.go

165 lines
3.1 KiB
Go
Raw Permalink Normal View History

2015-09-22 18:01:15 +03:00
package readline
import (
"bytes"
"container/list"
"fmt"
"io"
)
const (
S_STATE_FOUND = iota
S_STATE_FAILING
)
const (
S_DIR_BCK = iota
S_DIR_FWD
)
type opSearch struct {
2015-09-23 09:34:58 +03:00
inMode bool
state int
dir int
source *list.Element
w io.Writer
buf *RuneBuffer
data []rune
history *opHistory
cfg *Config
2015-09-23 09:34:58 +03:00
markStart int
markEnd int
2016-03-13 13:32:48 +03:00
width int
2015-09-22 18:01:15 +03:00
}
2016-03-13 13:32:48 +03:00
func newOpSearch(w io.Writer, buf *RuneBuffer, history *opHistory, cfg *Config, width int) *opSearch {
2015-09-22 18:01:15 +03:00
return &opSearch{
w: w,
buf: buf,
2016-02-21 05:14:19 +03:00
cfg: cfg,
2015-09-22 18:01:15 +03:00
history: history,
2016-03-13 13:32:48 +03:00
width: width,
2015-09-22 18:01:15 +03:00
}
}
2016-03-13 13:32:48 +03:00
func (o *opSearch) OnWidthChange(newWidth int) {
o.width = newWidth
}
2015-09-22 18:01:15 +03:00
func (o *opSearch) IsSearchMode() bool {
return o.inMode
}
func (o *opSearch) SearchBackspace() {
if len(o.data) > 0 {
o.data = o.data[:len(o.data)-1]
2015-09-23 06:10:36 +03:00
o.search(true)
2015-09-22 18:01:15 +03:00
}
}
2015-09-23 06:10:36 +03:00
func (o *opSearch) findHistoryBy(isNewSearch bool) (int, *list.Element) {
2015-09-22 18:01:15 +03:00
if o.dir == S_DIR_BCK {
return o.history.FindBck(isNewSearch, o.data, o.buf.idx)
2015-09-22 18:01:15 +03:00
}
return o.history.FindFwd(isNewSearch, o.data, o.buf.idx)
2015-09-22 18:01:15 +03:00
}
2015-09-23 06:10:36 +03:00
func (o *opSearch) search(isChange bool) bool {
if len(o.data) == 0 {
o.state = S_STATE_FOUND
o.SearchRefresh(-1)
return true
}
idx, elem := o.findHistoryBy(isChange)
2015-09-22 18:01:15 +03:00
if elem == nil {
o.SearchRefresh(-2)
return false
}
o.history.current = elem
2015-09-23 06:10:36 +03:00
item := o.history.showItem(o.history.current.Value)
start, end := 0, 0
if o.dir == S_DIR_BCK {
start, end = idx, idx+len(o.data)
} else {
start, end = idx, idx+len(o.data)
idx += len(o.data)
}
o.buf.SetWithIdx(idx, item)
2015-09-23 09:34:58 +03:00
o.markStart, o.markEnd = start, end
2015-09-22 18:01:15 +03:00
o.SearchRefresh(idx)
return true
}
func (o *opSearch) SearchChar(r rune) {
o.data = append(o.data, r)
2015-09-23 06:10:36 +03:00
o.search(true)
2015-09-22 18:01:15 +03:00
}
func (o *opSearch) SearchMode(dir int) bool {
if o.width == 0 {
return false
}
2015-09-23 06:10:36 +03:00
alreadyInMode := o.inMode
2015-09-22 18:01:15 +03:00
o.inMode = true
o.dir = dir
o.source = o.history.current
2015-09-23 06:10:36 +03:00
if alreadyInMode {
o.search(false)
} else {
o.SearchRefresh(-1)
}
return true
2015-09-22 18:01:15 +03:00
}
func (o *opSearch) ExitSearchMode(revert bool) {
if revert {
o.history.current = o.source
o.buf.Set(o.history.showItem(o.history.current.Value))
}
2015-09-23 09:34:58 +03:00
o.markStart, o.markEnd = 0, 0
2015-09-23 06:28:50 +03:00
o.state = S_STATE_FOUND
2015-09-22 18:01:15 +03:00
o.inMode = false
o.source = nil
o.data = nil
}
func (o *opSearch) SearchRefresh(x int) {
if x == -2 {
o.state = S_STATE_FAILING
} else if x >= 0 {
o.state = S_STATE_FOUND
}
if x < 0 {
x = o.buf.idx
}
x = o.buf.CurrentWidth(x)
2015-09-23 09:52:45 +03:00
x += o.buf.PromptLen()
2016-03-13 13:32:48 +03:00
x = x % o.width
2015-09-22 18:01:15 +03:00
2015-09-23 09:34:58 +03:00
if o.markStart > 0 {
2015-09-29 12:49:58 +03:00
o.buf.SetStyle(o.markStart, o.markEnd, "4")
2015-09-23 09:34:58 +03:00
}
2015-09-22 18:01:15 +03:00
lineCnt := o.buf.CursorLineCount()
buf := bytes.NewBuffer(nil)
buf.Write(bytes.Repeat([]byte("\n"), lineCnt))
buf.WriteString("\033[J")
if o.state == S_STATE_FAILING {
buf.WriteString("failing ")
}
if o.dir == S_DIR_BCK {
buf.WriteString("bck")
} else if o.dir == S_DIR_FWD {
buf.WriteString("fwd")
}
buf.WriteString("-i-search: ")
buf.WriteString(string(o.data)) // keyword
buf.WriteString("\033[4m \033[0m") // _
fmt.Fprintf(buf, "\r\033[%dA", lineCnt) // move prev
if x > 0 {
fmt.Fprintf(buf, "\033[%dC", x) // move forward
}
o.w.Write(buf.Bytes())
}