chore: improve diff view performance

This commit is contained in:
Kujtim Hoxha
2025-07-22 12:36:01 +02:00
parent bb4420f05b
commit 4db80f07e1

View File

@@ -1,6 +1,7 @@
package diffview
import (
"crypto/sha256"
"fmt"
"image/color"
"strconv"
@@ -59,6 +60,13 @@ type DiffView struct {
extraColOnAfter bool // add extra column on after panel
beforeNumDigits int
afterNumDigits int
// Cache lexer to avoid expensive file pattern matching on every line
cachedLexer chroma.Lexer
// Cache highlighted lines to avoid re-highlighting the same content
// Key: hash of (content + background color), Value: highlighted string
syntaxCache map[string]string
}
// New creates a new DiffView with default settings.
@@ -68,6 +76,7 @@ func New() *DiffView {
contextLines: udiff.DefaultContextLines,
lineNumbers: true,
tabWidth: 8,
syntaxCache: make(map[string]string),
}
dv.style = DefaultDarkStyle()
return dv
@@ -88,15 +97,26 @@ func (dv *DiffView) Split() *DiffView {
// Before sets the "before" file for the DiffView.
func (dv *DiffView) Before(path, content string) *DiffView {
dv.before = file{path: path, content: content}
// Clear caches when content changes
dv.clearCaches()
return dv
}
// After sets the "after" file for the DiffView.
func (dv *DiffView) After(path, content string) *DiffView {
dv.after = file{path: path, content: content}
// Clear caches when content changes
dv.clearCaches()
return dv
}
// clearCaches clears all caches when content or major settings change.
func (dv *DiffView) clearCaches() {
dv.cachedLexer = nil
dv.clearSyntaxCache()
dv.isComputed = false
}
// ContextLines sets the number of context lines for the DiffView.
func (dv *DiffView) ContextLines(contextLines int) *DiffView {
dv.contextLines = contextLines
@@ -156,9 +176,21 @@ func (dv *DiffView) TabWidth(tabWidth int) *DiffView {
// If nil, no syntax highlighting will be applied.
func (dv *DiffView) ChromaStyle(style *chroma.Style) *DiffView {
dv.chromaStyle = style
// Clear syntax cache when style changes since highlighting will be different
dv.clearSyntaxCache()
return dv
}
// clearSyntaxCache clears the syntax highlighting cache.
func (dv *DiffView) clearSyntaxCache() {
if dv.syntaxCache != nil {
// Clear the map but keep it allocated
for k := range dv.syntaxCache {
delete(dv.syntaxCache, k)
}
}
}
// String returns the string representation of the DiffView.
func (dv *DiffView) String() string {
dv.replaceTabs()
@@ -700,7 +732,15 @@ func (dv *DiffView) hightlightCode(source string, bgColor color.Color) string {
return source
}
l := dv.getChromaLexer(source)
// Create cache key from content and background color
cacheKey := dv.createSyntaxCacheKey(source, bgColor)
// Check if we already have this highlighted
if cached, exists := dv.syntaxCache[cacheKey]; exists {
return cached
}
l := dv.getChromaLexer()
f := dv.getChromaFormatter(bgColor)
it, err := l.Tokenise(nil, source)
@@ -712,22 +752,47 @@ func (dv *DiffView) hightlightCode(source string, bgColor color.Color) string {
if err := f.Format(&b, dv.chromaStyle, it); err != nil {
return source
}
return b.String()
result := b.String()
// Cache the result for future use
dv.syntaxCache[cacheKey] = result
return result
}
func (dv *DiffView) getChromaLexer(source string) chroma.Lexer {
// createSyntaxCacheKey creates a cache key from source content and background color.
// We use a simple hash to keep memory usage reasonable.
func (dv *DiffView) createSyntaxCacheKey(source string, bgColor color.Color) string {
// Convert color to string representation
r, g, b, a := bgColor.RGBA()
colorStr := fmt.Sprintf("%d,%d,%d,%d", r, g, b, a)
// Create a hash of the content + color to use as cache key
h := sha256.New()
h.Write([]byte(source))
h.Write([]byte(colorStr))
return fmt.Sprintf("%x", h.Sum(nil))
}
func (dv *DiffView) getChromaLexer() chroma.Lexer {
if dv.cachedLexer != nil {
return dv.cachedLexer
}
l := lexers.Match(dv.before.path)
if l == nil {
l = lexers.Analyse(source)
l = lexers.Analyse(dv.before.content)
}
if l == nil {
l = lexers.Fallback
}
return chroma.Coalesce(l)
dv.cachedLexer = chroma.Coalesce(l)
return dv.cachedLexer
}
func (dv *DiffView) getChromaFormatter(gbColor color.Color) chroma.Formatter {
func (dv *DiffView) getChromaFormatter(bgColor color.Color) chroma.Formatter {
return chromaFormatter{
bgColor: gbColor,
bgColor: bgColor,
}
}