fix: scrolling on suggestions
This commit is contained in:
@@ -2,10 +2,15 @@ package main
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/charmbracelet/x/ansi"
|
||||
)
|
||||
|
||||
const suggestionRenderCacheLimit = 256
|
||||
|
||||
var renderedSuggestions = newSuggestionRenderCache(suggestionRenderCacheLimit)
|
||||
|
||||
type parsedCommentBody struct {
|
||||
Prose string
|
||||
Suggestions []string
|
||||
@@ -16,6 +21,98 @@ type codeRange struct {
|
||||
End int
|
||||
}
|
||||
|
||||
type suggestionRenderCacheKey struct {
|
||||
path string
|
||||
removed string
|
||||
removedLines int
|
||||
replacement string
|
||||
width int
|
||||
}
|
||||
|
||||
type suggestionRenderCacheEntry struct {
|
||||
removed []detailLine
|
||||
added []detailLine
|
||||
}
|
||||
|
||||
type suggestionRenderCache struct {
|
||||
mu sync.Mutex
|
||||
limit int
|
||||
entries map[suggestionRenderCacheKey]suggestionRenderCacheEntry
|
||||
order []suggestionRenderCacheKey
|
||||
}
|
||||
|
||||
func newSuggestionRenderCache(limit int) *suggestionRenderCache {
|
||||
return &suggestionRenderCache{
|
||||
limit: limit, entries: make(map[suggestionRenderCacheKey]suggestionRenderCacheEntry),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *suggestionRenderCache) get(
|
||||
key suggestionRenderCacheKey,
|
||||
) (suggestionRenderCacheEntry, bool) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
entry, ok := c.entries[key]
|
||||
return cloneSuggestionRenderEntry(entry), ok
|
||||
}
|
||||
|
||||
func (c *suggestionRenderCache) put(
|
||||
key suggestionRenderCacheKey, entry suggestionRenderCacheEntry,
|
||||
) suggestionRenderCacheEntry {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
if cached, ok := c.entries[key]; ok {
|
||||
return cloneSuggestionRenderEntry(cached)
|
||||
}
|
||||
if len(c.entries) >= c.limit {
|
||||
delete(c.entries, c.order[0])
|
||||
c.order = c.order[1:]
|
||||
}
|
||||
c.entries[key] = cloneSuggestionRenderEntry(entry)
|
||||
c.order = append(c.order, key)
|
||||
return cloneSuggestionRenderEntry(entry)
|
||||
}
|
||||
|
||||
func (c *suggestionRenderCache) clear() {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
c.entries = make(map[suggestionRenderCacheKey]suggestionRenderCacheEntry)
|
||||
c.order = nil
|
||||
}
|
||||
|
||||
func cloneSuggestionRenderEntry(entry suggestionRenderCacheEntry) suggestionRenderCacheEntry {
|
||||
return suggestionRenderCacheEntry{
|
||||
removed: append([]detailLine(nil), entry.removed...),
|
||||
added: append([]detailLine(nil), entry.added...),
|
||||
}
|
||||
}
|
||||
|
||||
func renderSuggestion(
|
||||
path string, reviewed []string, replacement string, width int,
|
||||
) suggestionRenderCacheEntry {
|
||||
key := suggestionRenderCacheKey{
|
||||
path: path, removed: strings.Join(reviewed, "\n"), removedLines: len(reviewed),
|
||||
replacement: replacement, width: width,
|
||||
}
|
||||
if cached, ok := renderedSuggestions.get(key); ok {
|
||||
return cached
|
||||
}
|
||||
removed, added := normalizeSuggestion(reviewed, replacement)
|
||||
removedRanges, addedRanges := suggestionChangedRanges(removed, added)
|
||||
entry := suggestionRenderCacheEntry{}
|
||||
for index, source := range removed {
|
||||
entry.removed = append(entry.removed, wrapSuggestionLine(
|
||||
path, source, '-', removedRanges[index], width,
|
||||
)...)
|
||||
}
|
||||
for index, source := range added {
|
||||
entry.added = append(entry.added, wrapSuggestionLine(
|
||||
path, source, '+', addedRanges[index], width,
|
||||
)...)
|
||||
}
|
||||
return renderedSuggestions.put(key, entry)
|
||||
}
|
||||
|
||||
func parseCommentBody(body string) parsedCommentBody {
|
||||
var (
|
||||
result parsedCommentBody
|
||||
|
||||
@@ -85,6 +85,27 @@ func TestDetailRendersSuggestionAsRemovalAndAddition(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRenderedSuggestionsAreCachedWithoutSharingMutableLines(t *testing.T) {
|
||||
renderedSuggestions.clear()
|
||||
reviewed := []string{"old_value = compute()", "return old_value"}
|
||||
first := renderSuggestion(
|
||||
"example.py", reviewed, "new_value = compute()\nreturn new_value", 50,
|
||||
)
|
||||
if len(renderedSuggestions.entries) != 1 {
|
||||
t.Fatalf("suggestion cache entries = %d, want 1", len(renderedSuggestions.entries))
|
||||
}
|
||||
first.removed[0].rail = "mutated"
|
||||
second := renderSuggestion(
|
||||
"example.py", reviewed, "new_value = compute()\nreturn new_value", 50,
|
||||
)
|
||||
if second.removed[0].rail != "" {
|
||||
t.Fatal("caller mutation changed cached suggestion lines")
|
||||
}
|
||||
if len(renderedSuggestions.entries) != 1 {
|
||||
t.Fatalf("cache miss for unchanged suggestion: %d entries", len(renderedSuggestions.entries))
|
||||
}
|
||||
}
|
||||
|
||||
func TestSuggestionBackgroundIsDirectionalWithoutTextUnderline(t *testing.T) {
|
||||
removed := suggestionHighlight(" - ", "old", 12, '-')
|
||||
added := suggestionHighlight(" + ", "new", 12, '+')
|
||||
|
||||
1
theme.go
1
theme.go
@@ -102,6 +102,7 @@ func applyTheme(name string, custom ...CustomThemeConfig) error {
|
||||
currentThemeName = name
|
||||
commentMarkdownRenderers.Clear()
|
||||
commentMarkdownLines.clear()
|
||||
renderedSuggestions.clear()
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
21
tui.go
21
tui.go
@@ -3766,27 +3766,16 @@ func (m App) detailLines(width int) []detailLine {
|
||||
}
|
||||
}
|
||||
for suggestionIndex, suggestion := range content.Suggestions {
|
||||
removed, added := normalizeSuggestion(reviewedSourceLines(thread, comment), suggestion)
|
||||
removedRanges, addedRanges := suggestionChangedRanges(removed, added)
|
||||
label := "suggested change"
|
||||
if len(content.Suggestions) > 1 {
|
||||
label = fmt.Sprintf("suggested change %d/%d", suggestionIndex+1, len(content.Suggestions))
|
||||
}
|
||||
lines = append(lines, detailLine{rail: rail}, detailLine{rail: rail, text: dimStyle.Render(label)})
|
||||
for sourceIndex, source := range removed {
|
||||
lines = append(lines, addCommentRail(
|
||||
wrapSuggestionLine(
|
||||
thread.Path, source, '-', removedRanges[sourceIndex], max(1, width-4),
|
||||
), rail,
|
||||
)...)
|
||||
}
|
||||
for sourceIndex, source := range added {
|
||||
lines = append(lines, addCommentRail(
|
||||
wrapSuggestionLine(
|
||||
thread.Path, source, '+', addedRanges[sourceIndex], max(1, width-4),
|
||||
), rail,
|
||||
)...)
|
||||
}
|
||||
rendered := renderSuggestion(
|
||||
thread.Path, reviewedSourceLines(thread, comment), suggestion, max(1, width-4),
|
||||
)
|
||||
lines = append(lines, addCommentRail(rendered.removed, rail)...)
|
||||
lines = append(lines, addCommentRail(rendered.added, rail)...)
|
||||
}
|
||||
for reactionIndex, reactionLine := range renderReactionSummary(
|
||||
comment.Reactions, max(1, width-4),
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
package main
|
||||
|
||||
const dipleVersion = "0.3.1"
|
||||
const dipleVersion = "0.3.2"
|
||||
|
||||
Reference in New Issue
Block a user