1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2025-06-27 00:51:18 +02:00

Use model searching in commits (and sub-commits) view

This commit is contained in:
Stefan Haller
2024-05-31 17:24:50 +02:00
parent 779e6f95a3
commit 6a6316cfb6
75 changed files with 9066 additions and 4175 deletions

View File

@ -184,7 +184,8 @@ func (v *View) clearViewLines() {
type searcher struct {
searchString string
searchPositions []cellPos
searchPositions []SearchPosition
modelSearchResults []SearchPosition
currentSearchIndex int
onSelectItem func(int, int, int) error
}
@ -228,7 +229,7 @@ func (v *View) SelectSearchResult(index int) error {
index = itemCount - 1
}
y := v.searcher.searchPositions[index].y
y := v.searcher.searchPositions[index].Y
v.FocusPoint(v.ox, y)
if v.searcher.onSelectItem != nil {
@ -242,9 +243,22 @@ func (v *View) GetSearchStatus() (int, int) {
return v.searcher.currentSearchIndex, len(v.searcher.searchPositions)
}
func (v *View) Search(str string) error {
// modelSearchResults is optional; pass nil to search the view. If non-nil,
// these positions will be used for highlighting search results. Even in this
// case the view will still be searched on a per-line basis, so that the caller
// doesn't have to make assumptions where in the rendered line the search result
// is. The XStart and XEnd values in the modelSearchResults are only used in
// case the search string is not found in the given line, which can happen if
// the view renders an abbreviated version of some of the model data.
//
// Mind the difference between nil and empty slice: nil means we're not
// searching the model, empty slice means we *are* searching the model but we
// didn't find any matches.
func (v *View) UpdateSearchResults(str string, modelSearchResults []SearchPosition) {
v.writeMutex.Lock()
v.searcher.search(str)
defer v.writeMutex.Unlock()
v.searcher.search(str, modelSearchResults)
v.updateSearchPositions()
if len(v.searcher.searchPositions) > 0 {
@ -253,20 +267,25 @@ func (v *View) Search(str string) error {
adjustedY := v.oy + v.cy
adjustedX := v.ox + v.cx
for i, pos := range v.searcher.searchPositions {
if pos.y > adjustedY || (pos.y == adjustedY && pos.x > adjustedX) {
if pos.Y > adjustedY || (pos.Y == adjustedY && pos.XStart > adjustedX) {
currentIndex = i
break
}
}
v.searcher.currentSearchIndex = currentIndex
v.writeMutex.Unlock()
return v.SelectSearchResult(currentIndex)
} else {
v.writeMutex.Unlock()
return v.searcher.onSelectItem(-1, -1, 0)
}
}
func (v *View) Search(str string, modelSearchResults []SearchPosition) error {
v.UpdateSearchResults(str, modelSearchResults)
if len(v.searcher.searchPositions) > 0 {
return v.SelectSearchResult(v.searcher.currentSearchIndex)
}
return v.searcher.onSelectItem(-1, -1, 0)
}
func (v *View) ClearSearch() {
v.searcher.clearSearch()
}
@ -324,21 +343,23 @@ func calculateNewOrigin(selectedLine int, oldOrigin int, lineCount int, viewHeig
return oldOrigin
}
func (s *searcher) search(str string) {
func (s *searcher) search(str string, modelSearchResults []SearchPosition) {
s.searchString = str
s.searchPositions = []cellPos{}
s.searchPositions = []SearchPosition{}
s.modelSearchResults = modelSearchResults
s.currentSearchIndex = 0
}
func (s *searcher) clearSearch() {
s.searchString = ""
s.searchPositions = []cellPos{}
s.searchPositions = []SearchPosition{}
s.currentSearchIndex = 0
}
type cellPos struct {
x int
y int
type SearchPosition struct {
XStart int
XEnd int
Y int
}
type viewLine struct {
@ -489,6 +510,14 @@ func (v *View) setRune(x, y int, ch rune, fgColor, bgColor Attribute) error {
}
}
if matched, selected := v.isPatternMatchedRune(x, y); matched {
if selected {
bgColor = ColorCyan
} else {
bgColor = ColorYellow
}
}
// Don't display NUL characters
if ch == 0 {
ch = ' '
@ -752,6 +781,8 @@ func (v *View) writeRunes(p []rune) {
}
}
}
v.updateSearchPositions()
}
// exported functions use the mutex. Non-exported functions are for internal use
@ -957,8 +988,11 @@ func (v *View) updateSearchPositions() {
normalizedSearchStr = strings.ToLower(v.searcher.searchString)
}
v.searcher.searchPositions = []cellPos{}
for y, line := range v.lines {
v.searcher.searchPositions = []SearchPosition{}
searchPositionsForLine := func(line []cell, y int) []SearchPosition {
var result []SearchPosition
searchStringWidth := runewidth.StringWidth(v.searcher.searchString)
x := 0
for startIdx, c := range line {
found := true
@ -975,10 +1009,46 @@ func (v *View) updateSearchPositions() {
offset += 1
}
if found {
v.searcher.searchPositions = append(v.searcher.searchPositions, cellPos{x: x, y: y})
result = append(result, SearchPosition{XStart: x, XEnd: x + searchStringWidth, Y: y})
}
x += runewidth.RuneWidth(c.chr)
}
return result
}
if v.searcher.modelSearchResults != nil {
for _, result := range v.searcher.modelSearchResults {
if result.Y >= len(v.lines) {
break
}
// If a view line exists for this line index:
if v.lines[result.Y] != nil {
// search this view line for the search string
positions := searchPositionsForLine(v.lines[result.Y], result.Y)
if len(positions) > 0 {
// If we found any occurrences, add them
v.searcher.searchPositions = append(v.searcher.searchPositions, positions...)
} else {
// Otherwise, the search string was found in the model
// but not in the view line; this can happen if the view
// renders only truncated versions of the model strings.
// In this case, add one search position with what the
// model search function returned.
v.searcher.searchPositions = append(v.searcher.searchPositions, result)
}
} else {
// We don't have a view line for this line index. Add a
// searchPosition anyway, just for the sake of being able to
// show the "n of m" search status. The X positions don't
// matter in this case.
v.searcher.searchPositions = append(v.searcher.searchPositions, SearchPosition{XStart: -1, XEnd: -1, Y: result.Y})
}
}
} else {
for y, line := range v.lines {
v.searcher.searchPositions = append(v.searcher.searchPositions, searchPositionsForLine(line, y)...)
}
}
}
}
@ -999,7 +1069,6 @@ func (v *View) draw() error {
v.clearRunes()
v.updateSearchPositions()
maxX, maxY := v.Size()
if v.Wrap {
@ -1103,13 +1172,6 @@ func (v *View) draw() error {
if bgColor == ColorDefault {
bgColor = v.BgColor
}
if matched, selected := v.isPatternMatchedRune(x, y); matched {
if selected {
bgColor = ColorCyan
} else {
bgColor = ColorYellow
}
}
if err := v.setRune(x, y, c.chr, fgColor, bgColor); err != nil {
return err
@ -1137,11 +1199,10 @@ func (v *View) viewLineLengthIgnoringTrailingBlankLines() int {
}
func (v *View) isPatternMatchedRune(x, y int) (bool, bool) {
searchStringWidth := runewidth.StringWidth(v.searcher.searchString)
for i, pos := range v.searcher.searchPositions {
adjustedY := y + v.oy
adjustedX := x + v.ox
if adjustedY == pos.y && adjustedX >= pos.x && adjustedX < pos.x+searchStringWidth {
if adjustedY == pos.Y && adjustedX >= pos.XStart && adjustedX < pos.XEnd {
return true, i == v.searcher.currentSearchIndex
}
}