diff --git a/history.go b/history.go index 63b8f5f..e65b300 100644 --- a/history.go +++ b/history.go @@ -58,9 +58,19 @@ func (o *opHistory) Close() { } } -func (o *opHistory) FindHistoryBck(rs []rune) (int, *list.Element) { +func (o *opHistory) FindHistoryBck(isNewSearch bool, rs []rune, start int) (int, *list.Element) { for elem := o.current; elem != nil; elem = elem.Prev() { - idx := RunesIndex(o.showItem(elem.Value), rs) + item := o.showItem(elem.Value) + if isNewSearch { + start += len(rs) + } + if elem == o.current { + if len(item) < start { + continue + } + item = item[:start] + } + idx := RunesIndexBck(item, rs) if idx < 0 { continue } @@ -69,12 +79,25 @@ func (o *opHistory) FindHistoryBck(rs []rune) (int, *list.Element) { return -1, nil } -func (o *opHistory) FindHistoryFwd(rs []rune) (int, *list.Element) { +func (o *opHistory) FindHistoryFwd(isNewSearch bool, rs []rune, start int) (int, *list.Element) { for elem := o.current; elem != nil; elem = elem.Next() { - idx := RunesIndex(o.showItem(elem.Value), rs) + item := o.showItem(elem.Value) + if isNewSearch { + start -= len(rs) + } + if elem == o.current { + if len(item)-1 < start { + continue + } + item = item[start:] + } + idx := RunesIndex(item, rs) if idx < 0 { continue } + if elem == o.current { + idx += start + } return idx, elem } return -1, nil @@ -119,8 +142,8 @@ func (o *opHistory) NewHistory(current []rune) { if back := o.history.Back(); back != nil { prev := back.Prev() if prev != nil { - use := o.current.Value.(*HisItem) - if equalRunes(use.Tmp, prev.Value.(*HisItem).Source) { + use := o.showItem(o.current.Value.(*HisItem)) + if equalRunes(use, prev.Value.(*HisItem).Source) { o.current = o.history.Back() o.current.Value.(*HisItem).Clean() o.historyVer++ diff --git a/operation.go b/operation.go index b0f6a20..96e3bf1 100644 --- a/operation.go +++ b/operation.go @@ -96,6 +96,9 @@ func (l *Operation) ioloop() { case MetaBackspace: l.buf.BackEscapeWord() case CharEnter, CharEnter2: + if l.IsSearchMode() { + l.ExitSearchMode(false) + } l.buf.MoveToLineEnd() l.buf.WriteRune('\n') data := l.buf.Reset() @@ -136,7 +139,9 @@ func (l *Operation) ioloop() { l.ExitSearchMode(false) l.buf.Refresh() } - l.UpdateHistory(l.buf.Runes(), false) + if !l.IsSearchMode() { + l.UpdateHistory(l.buf.Runes(), false) + } } } diff --git a/runebuf.go b/runebuf.go index 06d0d39..3873243 100644 --- a/runebuf.go +++ b/runebuf.go @@ -198,7 +198,9 @@ func (r *RuneBuffer) Output() []byte { buf.Write(r.CleanOutput()) buf.Write(r.prompt) buf.Write([]byte(string(r.buf))) - buf.Write(bytes.Repeat([]byte{'\b'}, len(r.buf)-r.idx)) + if len(r.buf) > r.idx { + buf.Write(bytes.Repeat([]byte{'\b'}, len(r.buf)-r.idx)) + } return buf.Bytes() } @@ -224,6 +226,31 @@ func (r *RuneBuffer) Reset() []rune { return ret } +func (r *RuneBuffer) SetStyle(start, end int, style string) { + idx := r.idx + if end < start { + panic("end < start") + } + + // goto start + move := start - idx + if move > 0 { + r.w.Write([]byte(string(r.buf[r.idx : r.idx+move]))) + } else { + r.w.Write(bytes.Repeat([]byte("\b"), -move)) + } + r.w.Write([]byte("\033[" + style)) + r.w.Write([]byte(string(r.buf[start:end]))) + r.w.Write([]byte("\033[0m")) + if move > 0 { + r.w.Write(bytes.Repeat([]byte("\b"), -move+(end-start))) + } else if -move < end-start { + r.w.Write(bytes.Repeat([]byte("\b"), -move)) + } else { + r.w.Write([]byte(string(r.buf[end:r.idx]))) + } +} + func (r *RuneBuffer) SetWithIdx(idx int, buf []rune) { r.buf = buf r.idx = idx diff --git a/search.go b/search.go index 1ae82a5..c9421e8 100644 --- a/search.go +++ b/search.go @@ -43,39 +43,59 @@ func (o *opSearch) IsSearchMode() bool { func (o *opSearch) SearchBackspace() { if len(o.data) > 0 { o.data = o.data[:len(o.data)-1] - o.search() + o.search(true) } } -func (o *opSearch) findHistoryBy() (int, *list.Element) { +func (o *opSearch) findHistoryBy(isNewSearch bool) (int, *list.Element) { if o.dir == S_DIR_BCK { - return o.history.FindHistoryBck(o.data) + return o.history.FindHistoryBck(isNewSearch, o.data, o.buf.idx) } - return o.history.FindHistoryFwd(o.data) + return o.history.FindHistoryFwd(isNewSearch, o.data, o.buf.idx) } -func (o *opSearch) search() bool { - idx, elem := o.findHistoryBy() +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) if elem == nil { o.SearchRefresh(-2) return false } o.history.current = elem - o.buf.SetWithIdx(idx, o.history.showItem(o.history.current.Value)) + + 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) + o.buf.SetStyle(start, end, "4m") o.SearchRefresh(idx) return true } func (o *opSearch) SearchChar(r rune) { o.data = append(o.data, r) - o.search() + o.search(true) } func (o *opSearch) SearchMode(dir int) { + alreadyInMode := o.inMode o.inMode = true o.dir = dir o.source = o.history.current - o.SearchRefresh(-1) + if alreadyInMode { + o.search(false) + } else { + o.SearchRefresh(-1) + } } func (o *opSearch) ExitSearchMode(revert bool) { diff --git a/utils.go b/utils.go index be98f28..1d6f985 100644 --- a/utils.go +++ b/utils.go @@ -131,6 +131,22 @@ func RunesWidth(r []rune) (length int) { return } +func RunesIndexBck(r, sub []rune) int { + for i := len(r) - len(sub); i >= 0; i-- { + found := true + for j := 0; j < len(sub); j++ { + if r[i+j] != sub[j] { + found = false + break + } + } + if found { + return i + } + } + return -1 +} + func RunesIndex(r, sub []rune) int { for i := 0; i < len(r); i++ { found := true