From 0d34f0e6a34710cb0f589b44a86cef206d203dd9 Mon Sep 17 00:00:00 2001 From: Ayman Bagabas Date: Sun, 22 Dec 2024 10:48:19 +0300 Subject: [PATCH] fix(cellbuf): accurately calculate lines hashes --- cellbuf/hardscroll.go | 44 +++++++++++++++++++------------------------ cellbuf/hashmap.go | 30 ++++++++++++++--------------- cellbuf/window.go | 43 +++++++++++++++++++++--------------------- 3 files changed, 54 insertions(+), 63 deletions(-) diff --git a/cellbuf/hardscroll.go b/cellbuf/hardscroll.go index b5573706..bbdab1b2 100644 --- a/cellbuf/hardscroll.go +++ b/cellbuf/hardscroll.go @@ -11,14 +11,7 @@ import ( func (s *Screen) scrollOptimize() { height := s.newbuf.Height() if s.oldnum == nil || len(s.oldnum) < height { - var needLines int - if len(s.oldnum) < height { - needLines = height - } else { - needLines = len(s.oldnum) - } - - s.oldnum = make([]int, needLines) + s.oldnum = make([]int, height) } // Calculate the indices @@ -76,6 +69,11 @@ func (s *Screen) scrollOptimize() { // scrolln scrolls the screen up by n lines. func (s *Screen) scrolln(n, top, bot, maxY int) (v bool) { + const ( + nonDestScrollRegion = false + memoryBelow = false + ) + blank := s.clearBlank() if n > 0 { // Scroll up (forward) @@ -95,14 +93,15 @@ func (s *Screen) scrolln(n, top, bot, maxY int) (v bool) { } // Clear newly shifted-in lines. - if v { + if v && + (nonDestScrollRegion || (memoryBelow && bot == maxY)) { if bot == maxY { s.move(0, bot-n+1) - s.clearToBottom(&BlankCell) + s.clearToBottom(nil) } else { for i := 0; i < n; i++ { s.move(0, bot-i) - s.clearToEnd(&BlankCell, false) + s.clearToEnd(nil, false) } } } @@ -123,10 +122,11 @@ func (s *Screen) scrolln(n, top, bot, maxY int) (v bool) { } // Clear newly shifted-in lines. - if v { + if v && + (nonDestScrollRegion || (memoryBelow && top == 0)) { for i := 0; i < -n; i++ { s.move(0, top+i) - s.clearToEnd(&BlankCell, false) + s.clearToEnd(nil, false) } } } @@ -154,10 +154,10 @@ func (s *Screen) scrollBuffer(b *Buffer, n, top, bot int, blank *Cell) { if n < 0 { // shift n lines downwards limit := top - n - for line := bot; line >= limit && n >= 0 && n >= top; line-- { + for line := bot; line >= limit && line >= 0 && line >= top; line-- { copy(b.Lines[line], b.Lines[line+n]) } - for line := top; line < limit && n <= b.Height()-1 && n <= bot; line++ { + for line := top; line < limit && line <= b.Height()-1 && line <= bot; line++ { b.FillRect(blank, Rect(0, line, b.Width(), 1)) } } @@ -165,10 +165,10 @@ func (s *Screen) scrollBuffer(b *Buffer, n, top, bot int, blank *Cell) { if n > 0 { // shift n lines upwards limit := bot - n - for line := top; line <= limit && n <= b.Height()-1 && n <= bot; line++ { + for line := top; line <= limit && line <= b.Height()-1 && line <= bot; line++ { copy(b.Lines[line], b.Lines[line+n]) } - for line := bot; line > limit && n >= 0 && n >= top; line-- { + for line := bot; line > limit && line >= 0 && line >= top; line-- { b.FillRect(blank, Rect(0, line, b.Width(), 1)) } } @@ -183,17 +183,11 @@ func (s *Screen) touchLine(width, height, y, n int, changed bool) { } for i := y; i < y+n && i < height; i++ { - chg, ok := s.touch[i] if changed { - chg.firstCell = 0 - chg.lastCell = width - 1 + s.touch[i] = lineData{firstCell: 0, lastCell: width - 1} } else { - if ok { - chg.firstCell = newIndex - chg.lastCell = newIndex - } + delete(s.touch, i) } - s.touch[i] = chg } } diff --git a/cellbuf/hashmap.go b/cellbuf/hashmap.go index 6c714806..0d25b549 100644 --- a/cellbuf/hashmap.go +++ b/cellbuf/hashmap.go @@ -31,24 +31,21 @@ const newIndex = -1 // updateHashmap updates the hashmap with the new hash value. func (s *Screen) updateHashmap() { height := s.newbuf.Height() - if height > len(s.hashtab) { - s.hashtab = make([]hashmap, height*2) - } - - if s.oldhash != nil && s.newhash != nil { + if len(s.oldhash) >= height && len(s.newhash) >= height { // rehash changed lines for i := 0; i < height; i++ { _, ok := s.touch[i] if ok { + s.oldhash[i] = hash(s.curbuf.Line(i)) s.newhash[i] = hash(s.newbuf.Line(i)) } } } else { // rehash all - if s.oldhash == nil { + if len(s.oldhash) != height { s.oldhash = make([]uint64, height) } - if s.newhash == nil { + if len(s.newhash) != height { s.newhash = make([]uint64, height) } for i := 0; i < height; i++ { @@ -94,7 +91,8 @@ func (s *Screen) updateHashmap() { } // Mark line pair corresponding to unique hash pairs. - for _, hsp := range s.hashtab { + for i := 0; i < len(s.hashtab) && s.hashtab[i].value != 0; i++ { + hsp := &s.hashtab[i] if hsp.oldcount == 1 && hsp.newcount == 1 && hsp.oldindex != hsp.newindex { s.oldnum[hsp.newindex] = hsp.oldindex } @@ -116,7 +114,7 @@ func (s *Screen) updateHashmap() { start = i shift = s.oldnum[i] - i i++ - for i < height && i < len(s.oldnum) && s.oldnum[i] != newIndex && s.oldnum[i]-i == shift { + for i < height && s.oldnum[i] != newIndex && s.oldnum[i]-i == shift { i++ } size = i - start @@ -134,7 +132,7 @@ func (s *Screen) updateHashmap() { // scrollOldhash func (s *Screen) scrollOldhash(n, top, bot int) { - if s.oldhash == nil { + if len(s.oldhash) == 0 { return } @@ -165,7 +163,7 @@ func (s *Screen) growHunks() { ) height := s.newbuf.Height() - for i < height && i < len(s.oldnum) && s.oldnum[i] == newIndex { + for i < height && s.oldnum[i] == newIndex { i++ } for ; i < height; i = nextHunk { @@ -179,20 +177,20 @@ func (s *Screen) growHunks() { // get forward limit i = start + 1 - for i < height && i < len(s.oldnum) && + for i < height && s.oldnum[i] != newIndex && s.oldnum[i]-i == shift { i++ } end = i - for i < height && i < len(s.oldnum) && s.oldnum[i] == newIndex { + for i < height && s.oldnum[i] == newIndex { i++ } nextHunk = i forwardLimit = i - if i >= height || (i < len(s.oldnum) && s.oldnum[i] >= i) { + if i >= height || s.oldnum[i] >= i { forwardRefLimit = i } else { forwardRefLimit = s.oldnum[i] @@ -284,7 +282,7 @@ func (s *Screen) costEffective(from, to int, blank bool) bool { func (s *Screen) updateCost(from, to Line) (cost int) { var fidx, tidx int - for i := s.newbuf.Width(); i > 0; i, fidx, tidx = i-1, fidx+1, tidx+1 { + for i := s.newbuf.Width() - 1; i > 0; i, fidx, tidx = i-1, fidx+1, tidx+1 { if !cellEqual(from.At(fidx), to.At(tidx)) { cost++ } @@ -294,7 +292,7 @@ func (s *Screen) updateCost(from, to Line) (cost int) { func (s *Screen) updateCostBlank(to Line) (cost int) { var tidx int - for i := s.newbuf.Width(); i > 0; i, tidx = i-1, tidx+1 { + for i := s.newbuf.Width() - 1; i > 0; i, tidx = i-1, tidx+1 { if !cellEqual(nil, to.At(tidx)) { cost++ } diff --git a/cellbuf/window.go b/cellbuf/window.go index 8ad55759..8cee79a1 100644 --- a/cellbuf/window.go +++ b/cellbuf/window.go @@ -332,29 +332,27 @@ func (s *Screen) Clear() bool { // ClearRect implements Window. func (s *Screen) ClearRect(r Rectangle) bool { - s.newbuf.ClearRect(r) - s.mu.Lock() - for i := r.Min.Y; i < r.Max.Y; i++ { - s.touch[i] = lineData{firstCell: r.Min.X, lastCell: r.Max.X - 1} - } - s.mu.Unlock() - return true + return s.FillRect(nil, r) } // Draw implements Window. func (s *Screen) Draw(x int, y int, cell *Cell) (v bool) { s.mu.Lock() + defer s.mu.Unlock() + cellWidth := 1 + if cell != nil { + cellWidth = cell.Width + } if prev := s.curbuf.Cell(x, y); !cellEqual(prev, cell) { chg, ok := s.touch[y] if !ok { - chg = lineData{firstCell: x, lastCell: x} + chg = lineData{firstCell: x, lastCell: x + cellWidth} } else { chg.firstCell = min(chg.firstCell, x) - chg.lastCell = max(chg.lastCell, x) + chg.lastCell = max(chg.lastCell, x+cellWidth) } s.touch[y] = chg } - s.mu.Unlock() return s.newbuf.Draw(x, y, cell) } @@ -366,12 +364,12 @@ func (s *Screen) Fill(cell *Cell) bool { // FillRect implements Window. func (s *Screen) FillRect(cell *Cell, r Rectangle) bool { - s.newbuf.FillRect(cell, r) s.mu.Lock() + defer s.mu.Unlock() + s.newbuf.FillRect(cell, r) for i := r.Min.Y; i < r.Max.Y; i++ { - s.touch[i] = lineData{firstCell: r.Min.X, lastCell: r.Max.X - 1} + s.touch[i] = lineData{firstCell: r.Min.X, lastCell: r.Max.X} } - s.mu.Unlock() return true } @@ -893,7 +891,7 @@ func (s *Screen) transformLine(y int) { } // Update the old line with the new line - if s.newbuf.Width() > firstCell && len(oldLine) != 0 { + if s.newbuf.Width() >= firstCell && len(oldLine) != 0 { copy(oldLine[firstCell:], newLine[firstCell:]) } } @@ -1099,8 +1097,8 @@ func (s *Screen) render() { nonEmpty = s.clearBottom(nonEmpty, partialClear) for i = 0; i < nonEmpty; i++ { - chg, wasTouched := s.touch[i] - if wasTouched && chg.firstCell != newIndex && chg.lastCell != newIndex { + _, ok := s.touch[i] + if ok { s.transformLine(i) changedLines++ } @@ -1108,7 +1106,7 @@ func (s *Screen) render() { } // Sync windows and screen - s.touch = make(map[int]lineData) + s.touch = make(map[int]lineData, s.newbuf.Height()) if s.curbuf.Width() != s.newbuf.Width() || s.curbuf.Height() != s.newbuf.Height() { // Resize the old buffer to match the new buffer. @@ -1185,7 +1183,7 @@ func (s *Screen) reset() { s.cur = Cursor{Position: undefinedPos} } s.saved = s.cur - s.touch = make(map[int]lineData) + s.touch = make(map[int]lineData, s.newbuf.Height()) if s.curbuf != nil { s.curbuf.Clear() } @@ -1209,20 +1207,21 @@ func (s *Screen) Resize(width, height int) bool { // Clear new columns and lines if width > oldh { - s.ClearRect(Rect(oldw-2, 0, width-oldw, height)) + s.ClearRect(Rect(max(oldw-2, 0), 0, width-oldw, height)) } else if width < oldw { - s.ClearRect(Rect(width-1, 0, oldw-width, height)) + s.ClearRect(Rect(max(width-1, 0), 0, oldw-width, height)) } if height > oldh { - s.ClearRect(Rect(0, oldh-1, width, height-oldh)) + s.ClearRect(Rect(0, max(oldh-1, 0), width, height-oldh)) } else if height < oldh { - s.ClearRect(Rect(0, height-1, width, oldh-height)) + s.ClearRect(Rect(0, max(height-1, 0), width, oldh-height)) } s.mu.Lock() s.newbuf.Resize(width, height) s.opts.Width, s.opts.Height = width, height + s.oldhash, s.newhash = nil, nil s.mu.Unlock() return true