Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 11 additions & 3 deletions gui.go
Original file line number Diff line number Diff line change
Expand Up @@ -190,9 +190,11 @@ type Gui struct {

OnSearchEscape func() error
// these keys must either be of type Key of rune
SearchEscapeKey any
NextSearchMatchKey any
PrevSearchMatchKey any
SearchEscapeKey any
NextSearchMatchKey any
PrevSearchMatchKey any
NextSearchMatchFromCursorKey any
PrevSearchMatchFromCursorKey any

ErrorHandler func(error) error

Expand Down Expand Up @@ -269,6 +271,8 @@ func NewGui(opts NewGuiOpts) (*Gui, error) {
g.SearchEscapeKey = KeyEsc
g.NextSearchMatchKey = 'n'
g.PrevSearchMatchKey = 'N'
g.NextSearchMatchFromCursorKey = ""
g.PrevSearchMatchFromCursorKey = ""

g.playRecording = opts.PlayRecording

Expand Down Expand Up @@ -1521,6 +1525,10 @@ func (g *Gui) execKeybindings(v *View, ev *GocuiEvent) error {
return v.gotoNextMatch()
} else if eventMatchesKey(ev, g.PrevSearchMatchKey) {
return v.gotoPreviousMatch()
} else if eventMatchesKey(ev, g.NextSearchMatchFromCursorKey) {
return v.gotoNextMatchFromCursor()
} else if eventMatchesKey(ev, g.PrevSearchMatchFromCursorKey) {
return v.gotoPreviousMatchFromCursor()
} else if eventMatchesKey(ev, g.SearchEscapeKey) {
v.searcher.clearSearch()
if g.OnSearchEscape != nil {
Expand Down
62 changes: 62 additions & 0 deletions view.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ package gocui
import (
"fmt"
"io"
"sort"
"strings"
"sync"
"unicode"
Expand Down Expand Up @@ -250,6 +251,67 @@ func (v *View) gotoPreviousMatch() error {
return v.SelectSearchResult(v.searcher.currentSearchIndex)
}

func (v *View) gotoNextMatchFromCursor() error {
if len(v.searcher.searchPositions) == 0 {
return nil
}

positions := v.searcher.searchPositions
currentIdx := v.searcher.currentSearchIndex
currentLine := v.SelectedLineIdx()

// If current match is on same line, check next position
if currentIdx >= 0 && currentIdx < len(positions) && positions[currentIdx].Y == currentLine {
if currentIdx+1 < len(positions) && positions[currentIdx+1].Y == currentLine {
v.searcher.currentSearchIndex = currentIdx + 1
return v.SelectSearchResult(currentIdx + 1)
}
}

// Find first match after current line
nextIndex := sort.Search(len(positions), func(i int) bool {
return positions[i].Y > currentLine
})

if nextIndex >= len(positions) {
nextIndex = 0
}

v.searcher.currentSearchIndex = nextIndex
return v.SelectSearchResult(nextIndex)
}

func (v *View) gotoPreviousMatchFromCursor() error {
if len(v.searcher.searchPositions) == 0 {
return nil
}

positions := v.searcher.searchPositions
currentIdx := v.searcher.currentSearchIndex
currentLine := v.SelectedLineIdx()

// If current match is on same line, check previous position
if currentIdx >= 0 && currentIdx < len(positions) && positions[currentIdx].Y == currentLine {
if currentIdx-1 >= 0 && positions[currentIdx-1].Y == currentLine {
v.searcher.currentSearchIndex = currentIdx - 1
return v.SelectSearchResult(currentIdx - 1)
}
}

// Find first match on or after current line, then go back one
idx := sort.Search(len(positions), func(i int) bool {
return positions[i].Y >= currentLine
})

prevIndex := idx - 1
if prevIndex < 0 {
prevIndex = len(positions) - 1
}

v.searcher.currentSearchIndex = prevIndex
return v.SelectSearchResult(prevIndex)
}

func (v *View) SelectSearchResult(index int) error {
itemCount := len(v.searcher.searchPositions)
if itemCount == 0 {
Expand Down